diff --git a/BUILDING.md b/BUILDING.md index 0ccc9a7f4..806b00afa 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,42 +1,42 @@ -# Building CoastalME from source - - -Build requirements --------------------------------------------------------------------------------- - -The minimum requirements to build CoastalME are: - -- CMake >= 3.16, and an associated build system (make, ninja, Visual Studio, etc.) -- GDAL >= 2.1 -- C++11 -- F2008 compiler - - -CMake (CoastalME versions >= 3.5.0) --------------------------------------------------------------------------------- - -Since version 3.5.0, CoastalME can be built using the CMake build system. -With the CMake build system you can compile and install CoastalME on more or less any -platform. After unpacking the source distribution archive (or cloning the repository) -step into the source tree: - - cd CoastalME-{VERSION} - -Create a build directory and step into it: - - - mkdir build - cd build - -From the build directory you can now configure CMake, build and install the binaries: - - - cmake .. - cmake --build . - cmake --build . --target install - -Note: - - For a minimal build, add these options to the initial ``cmake`` command: ``...``. - To enable specific DEBUG drivers, add ``....`` or ``...``. +# Building CoastalME from source + + +Build requirements +-------------------------------------------------------------------------------- + +The minimum requirements to build CoastalME are: + +- CMake >= 3.16, and an associated build system (make, ninja, Visual Studio, etc.) +- GDAL >= 2.1 +- C++11 +- F2008 compiler + + +CMake (CoastalME versions >= 3.5.0) +-------------------------------------------------------------------------------- + +Since version 3.5.0, CoastalME can be built using the CMake build system. +With the CMake build system you can compile and install CoastalME on more or less any +platform. After unpacking the source distribution archive (or cloning the repository) +step into the source tree: + + cd CoastalME-{VERSION} + +Create a build directory and step into it: + + + mkdir build + cd build + +From the build directory you can now configure CMake, build and install the binaries: + + + cmake .. + cmake --build . + cmake --build . --target install + +Note: + + For a minimal build, add these options to the initial ``cmake`` command: ``...``. + To enable specific DEBUG drivers, add ``....`` or ``...``. \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 83589b77c..1c661eb9e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,135 +1,135 @@ -# Code of Conduct - -This code of conduct governs how we behave in any CoastalME forum or event and -whenever we will be judged by our actions. We expect it to be honored by everyone -who participates in the CoastalME community formally or informally, or claims any -affiliation with the project. - -Introduction -------------- - -This code of conduct governs how we behave in any CoastalME forum or event and -whenever we will be judged by our actions. We expect it to be honored by -everyone who participates in the CoastalME community formally or informally, or -claims any affiliation with the CoastalME project. - -It applies to in-person events (such as conferences and related social events), -IRC, public and private mailing lists, the issue tracker, the wiki, blogs, -Twitter, and any other forums which the community uses for communication and -interactions. - -This code is not exhaustive or complete. It serves to distill our common -understanding of a collaborative, shared environment and goals. We expect it to -be followed in spirit as much as in the letter, so that it can enrich all of -us and the technical communities in which we participate. - -Diversity Statement -------------------- - -CoastalME welcomes and encourages participation by everyone. We are committed to -being a community that everyone feels good about joining, and we will always -work to treat everyone well. No matter how you identify yourself or how others -perceive you: we welcome you. -Specific Guidelines - -We strive to: - -- Be open. - - We invite anyone to participate in our community. We preferably use public - methods of communication for project-related messages, unless discussing - something sensitive. This applies to messages for help or project-related - support, too; not only is a public support request much more likely to result - in an answer to a question, it also makes sure that any inadvertent mistakes - made by people answering will be more easily detected and corrected. - -- Be empathetic, welcoming, friendly, and patient. - - We work together to resolve conflict, assume good intentions, and do our best - to act in an empathetic fashion. We may all experience some frustration from - time to time, but we do not allow frustration to turn into a personal attack. - A community where people feel uncomfortable or threatened is not a productive - one. Note that we have a multi-cultural, multi-lingual community and some of - us are non-native speakers. We should be respectful when dealing with other - community members as well as with people outside our community. - -- Be collaborative. - - Our work will be used by other people, and in turn we will depend on the work - of others. When we make something for the benefit of CoastalME, we are willing to - explain to others how it works, so that they can build on the work to make it - even better. Any decision we make will affect users and colleagues, and we - take those consequences seriously when making decisions. - -- Be inquisitive. - - Nobody knows everything! Asking questions early avoids many problems later, - so questions are encouraged, though they may be directed to the appropriate - forum. Those who are asked should be responsive and helpful, within the - context of our shared goal of improving CoastalME. - -- Be careful in the words that we choose. - - Whether we are participating as professionals or volunteers, we value - professionalism in all interactions, and take responsibility for our own - speech. Be kind to others. Do not insult or put down other participants. - -- Be concise. - - Keep in mind that what you write once will be read by hundreds of persons. - Writing a short email means people can understand the conversation as - efficiently as possible. Short emails should always strive to be empathetic, - welcoming, friendly and patient. When a long explanation is necessary, - consider adding a summary. - - Try to bring new ideas to a conversation so that each mail adds something - unique to the thread, keeping in mind that the rest of the thread still - contains the other messages with arguments that have already been made. - - Try to stay on topic, especially in discussions that are already fairly large. - -- Step down considerately. - - Members of every project come and go. When somebody leaves or disengages - from the project they should tell people they are leaving and take the proper - steps to ensure that others can pick up where they left off. In doing so, - they should remain respectful of those who continue to participate in the - project and should not misrepresent the project's goals or achievements. - Likewise, community members should respect any individual's choice to leave - the project. - -Anti-Harassment ---------------- - -Harassment and other exclusionary behavior are not acceptable. -This includes, but is not limited to: - -- Personal insults or discriminatory jokes and language, especially those using racist or sexist terms. -- Offensive comments, excessive or unnecessary profanity. -- Intimidation, violent threats or demands. -- Sustained disruption of sessions or events. -- Stalking, harassing photography or recording. -- Unwelcome physical contact or sexual attention. -- Repeated harassment of others. In general, if someone asks you to stop, then stop. -- Posting (or threatening to post) other people's personally identifying information ("doxing"). -- Sharing private content, such as emails sent privately or non-publicly, or unlogged forums such as IRC channel history. -- Advocating for, or encouraging, any of the above behavior. - -Reporting Guidelines --------------------- - -If you believe someone is breaking this code of conduct, you may reply to them, -and point to this code of conduct. Such messages may be in public or in private, -whatever is most appropriate. Assume good faith; it is more likely that -participants are unaware of their bad behavior than that they intentionally try -to degrade the quality of the discussion. Should there be difficulties in -dealing with the situation, you may report your concerns to event staff, a forum -leader or the CoastalME Board. Serious or persistent offenders may be expelled from -the event or forum by event organizers or forum leaders. - -To privately email the code of conduct committee leadership use coc@osgeo.org. - -Credits -------- - +# Code of Conduct + +This code of conduct governs how we behave in any CoastalME forum or event and +whenever we will be judged by our actions. We expect it to be honored by everyone +who participates in the CoastalME community formally or informally, or claims any +affiliation with the project. + +Introduction +------------- + +This code of conduct governs how we behave in any CoastalME forum or event and +whenever we will be judged by our actions. We expect it to be honored by +everyone who participates in the CoastalME community formally or informally, or +claims any affiliation with the CoastalME project. + +It applies to in-person events (such as conferences and related social events), +IRC, public and private mailing lists, the issue tracker, the wiki, blogs, +Twitter, and any other forums which the community uses for communication and +interactions. + +This code is not exhaustive or complete. It serves to distill our common +understanding of a collaborative, shared environment and goals. We expect it to +be followed in spirit as much as in the letter, so that it can enrich all of +us and the technical communities in which we participate. + +Diversity Statement +------------------- + +CoastalME welcomes and encourages participation by everyone. We are committed to +being a community that everyone feels good about joining, and we will always +work to treat everyone well. No matter how you identify yourself or how others +perceive you: we welcome you. +Specific Guidelines + +We strive to: + +- Be open. + + We invite anyone to participate in our community. We preferably use public + methods of communication for project-related messages, unless discussing + something sensitive. This applies to messages for help or project-related + support, too; not only is a public support request much more likely to result + in an answer to a question, it also makes sure that any inadvertent mistakes + made by people answering will be more easily detected and corrected. + +- Be empathetic, welcoming, friendly, and patient. + + We work together to resolve conflict, assume good intentions, and do our best + to act in an empathetic fashion. We may all experience some frustration from + time to time, but we do not allow frustration to turn into a personal attack. + A community where people feel uncomfortable or threatened is not a productive + one. Note that we have a multi-cultural, multi-lingual community and some of + us are non-native speakers. We should be respectful when dealing with other + community members as well as with people outside our community. + +- Be collaborative. + + Our work will be used by other people, and in turn we will depend on the work + of others. When we make something for the benefit of CoastalME, we are willing to + explain to others how it works, so that they can build on the work to make it + even better. Any decision we make will affect users and colleagues, and we + take those consequences seriously when making decisions. + +- Be inquisitive. + + Nobody knows everything! Asking questions early avoids many problems later, + so questions are encouraged, though they may be directed to the appropriate + forum. Those who are asked should be responsive and helpful, within the + context of our shared goal of improving CoastalME. + +- Be careful in the words that we choose. + + Whether we are participating as professionals or volunteers, we value + professionalism in all interactions, and take responsibility for our own + speech. Be kind to others. Do not insult or put down other participants. + +- Be concise. + + Keep in mind that what you write once will be read by hundreds of persons. + Writing a short email means people can understand the conversation as + efficiently as possible. Short emails should always strive to be empathetic, + welcoming, friendly and patient. When a long explanation is necessary, + consider adding a summary. + + Try to bring new ideas to a conversation so that each mail adds something + unique to the thread, keeping in mind that the rest of the thread still + contains the other messages with arguments that have already been made. + + Try to stay on topic, especially in discussions that are already fairly large. + +- Step down considerately. + + Members of every project come and go. When somebody leaves or disengages + from the project they should tell people they are leaving and take the proper + steps to ensure that others can pick up where they left off. In doing so, + they should remain respectful of those who continue to participate in the + project and should not misrepresent the project's goals or achievements. + Likewise, community members should respect any individual's choice to leave + the project. + +Anti-Harassment +--------------- + +Harassment and other exclusionary behavior are not acceptable. +This includes, but is not limited to: + +- Personal insults or discriminatory jokes and language, especially those using racist or sexist terms. +- Offensive comments, excessive or unnecessary profanity. +- Intimidation, violent threats or demands. +- Sustained disruption of sessions or events. +- Stalking, harassing photography or recording. +- Unwelcome physical contact or sexual attention. +- Repeated harassment of others. In general, if someone asks you to stop, then stop. +- Posting (or threatening to post) other people's personally identifying information ("doxing"). +- Sharing private content, such as emails sent privately or non-publicly, or unlogged forums such as IRC channel history. +- Advocating for, or encouraging, any of the above behavior. + +Reporting Guidelines +-------------------- + +If you believe someone is breaking this code of conduct, you may reply to them, +and point to this code of conduct. Such messages may be in public or in private, +whatever is most appropriate. Assume good faith; it is more likely that +participants are unaware of their bad behavior than that they intentionally try +to degrade the quality of the discussion. Should there be difficulties in +dealing with the situation, you may report your concerns to event staff, a forum +leader or the CoastalME Board. Serious or persistent offenders may be expelled from +the event or forum by event organizers or forum leaders. + +To privately email the code of conduct committee leadership use coc@osgeo.org. + +Credits +------- + CoastalME re-uses the `OSGeo Code of Conduct `__ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2d3a4041..824ac64c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,36 +1,36 @@ -# How to contribute? - -There are several ways for developers and users to contribute to CoastalME: - -- [Contributing code](#contributing-code) -- [Filing high-quality reports](#filing-high-quality-reports) -- [Improving documentation](#improving-documentation) -- [Credits](#credits) - -## Contributing code - -Minor changes to CoastalME, such as bug fixes, may be made by opening a GitHub pull request. - -Major changes should be discussed on the [CoastalME Discussions](https://github.com/apayo/CoastalME/discussions/19#discussion-7445785) and may require the drafting of a RFC (request for comment) document. - -CoastalME's policy on substantial code additions is documented at [rfc_process.md](/development/rfc_process.md). - -## Filing high-quality issue reports - -Using the GitHub [issue reports](https://github.com/apayo/CoastalME/issues) at the Master repository. - -## Improving documentation - -CoastalME's documentation includes C++ [API documentation](https://codedocs.xyz/apayo/CoastalME/) built automatically from source comments using Doxygen and [CodeDocx.XYZ](https://codedocs.xyz) files containing manually-edited content. - -To be correctly parsed, the documentation should follow the [Doxygen block documentation style](https://doxygen.nl/manual/docblocks.html#specialblock) - - /*! \brief Brief description. - * Brief description continued. - * - * Detailed description starts here. - */ - -## Credits - +# How to contribute? + +There are several ways for developers and users to contribute to CoastalME: + +- [Contributing code](#contributing-code) +- [Filing high-quality reports](#filing-high-quality-reports) +- [Improving documentation](#improving-documentation) +- [Credits](#credits) + +## Contributing code + +Minor changes to CoastalME, such as bug fixes, may be made by opening a GitHub pull request. + +Major changes should be discussed on the [CoastalME Discussions](https://github.com/apayo/CoastalME/discussions/19#discussion-7445785) and may require the drafting of a RFC (request for comment) document. + +CoastalME's policy on substantial code additions is documented at [rfc_process.md](/development/rfc_process.md). + +## Filing high-quality issue reports + +Using the GitHub [issue reports](https://github.com/apayo/CoastalME/issues) at the Master repository. + +## Improving documentation + +CoastalME's documentation includes C++ [API documentation](https://codedocs.xyz/apayo/CoastalME/) built automatically from source comments using Doxygen and [CodeDocx.XYZ](https://codedocs.xyz) files containing manually-edited content. + +To be correctly parsed, the documentation should follow the [Doxygen block documentation style](https://doxygen.nl/manual/docblocks.html#specialblock) + + /*! \brief Brief description. + * Brief description continued. + * + * Detailed description starts here. + */ + +## Credits + This section has been largely inspired by https://github.com/OSGeo/gdal \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 8566c3cc9..d929fb5e0 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,78 +1,78 @@ - -# CoastalME Licensing - -This file attempts to include all licenses that apply within the CoastalME -source tree, in particular any that are supposed to be exposed to the end user -for credit requirements for instance. The contents of this file can be -displayed from CostalME GitHub Master repository with filename LICENSE.MD - -- [CoastalME General](#coastalme-general) -- [GDAL-OGR General](#gdal-ogr-general) -- [CSHORE General](#cshore-general) - - -## CoastalME General - -The text shown below should be added to all source code. - - - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -https://github.com/apayo/CoastalME - -## GDAL-OGR General - -In general GDAL/OGR is licensed under an MIT style license with the -following terms: - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -https://github.com/OSGeo/gdal - -## CSHORE General - -MIT License - -Copyright (c) Microsoft Corporation. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - + +# CoastalME Licensing + +This file attempts to include all licenses that apply within the CoastalME +source tree, in particular any that are supposed to be exposed to the end user +for credit requirements for instance. The contents of this file can be +displayed from CostalME GitHub Master repository with filename LICENSE.MD + +- [CoastalME General](#coastalme-general) +- [GDAL-OGR General](#gdal-ogr-general) +- [CSHORE General](#cshore-general) + + +## CoastalME General + +The text shown below should be added to all source code. + + + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +https://github.com/apayo/CoastalME + +## GDAL-OGR General + +In general GDAL/OGR is licensed under an MIT style license with the +following terms: + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +https://github.com/OSGeo/gdal + +## CSHORE General + +MIT License + +Copyright (c) Microsoft Corporation. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE + https://github.com/erdc/cshore \ No newline at end of file diff --git a/TO_DO_LIST.md b/TO_DO_LIST.md index f42c643bc..32295d033 100644 --- a/TO_DO_LIST.md +++ b/TO_DO_LIST.md @@ -1,118 +1,118 @@ -# TO DO LIST - -089 is max (also using github permalinks to code lines) - -## Table of contents -- [Documentation]{#documentation) -- [User input](#user-input) -- [Error handling](#error-handling) -- [Theory/Efficiency](#theory-efficiency) -- [Output](#output) -- [Completed](#completed) - -## Documentation -- TODO 001 [Add more information about all classes] -- TODO 007 [Need more info re. setup and surge from Manuel] - -## User input -- TODO 000 [Should user input be split in two main files] -- TODO 011 Should this constant be a user input? - [1](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/cme.h#L600-L602) - [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.cpp#L211) - [3](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.cpp#L371) -- TODO 036 [Read in changed deep water wave values](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/init_grid.cpp#L177) -- TODO 030 [Do we also need to be able to input landform sub-categories?] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L750) -- TODO 027 [Sort out GDAL problem with raster reference units](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L537) -- TODO 022 [Get intervention update working](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_intervention.cpp#L32) -- TODO 042 [Should we have a smallest valid input for KLS in the CERC equation?] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/read_input.cpp#L2378-L2380) -- TODO 045 [Method of getting depth of closure value needs to be a user input] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.h#L771) -- TODO 049 [Handle other command line parameters e.g. path to .ini file, path to datafile](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/utils.cpp#L125) -- TODO 035 [Also handle other EPSG for vector spatial reference systems](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_vector.cpp#L492) -- TODO 054 [Choose more files to omit from "usual" raster output] -- TODO 069 [Enable ability to represent intervention structures which have their foundation embedded in consolidated sediment. In other words, with the elevation of the base of the intervention structure *below* the top of all consolidated sediment layers. Will need some sanity checking of elevations] -- TODO 071 [If the user input file format is changed, write a Python script to convert from the old file format to the new] -- TODO 083 [Get all three kinds of sediment input events working correctly] - -## Error handling -- TODO 038 [Do better error handling if insufficient memory] -- TODO 004 [Improve error handling of situation where we have a valid shadow zone but cannot find a neighbouring cell which is 'under' the coastline](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_shadow_zones.cpp#L490) -- TODO 006 [Check GDALGridCreate() with only start-of-coast or an end-of-coast profiles](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L214) -- TODO 009 [Decide what to do when we have eroded down to basement](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L214) -- TODO 017 [Extra safety check needed, make sure that each point is within valid grid](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L146) -- TODO 018 [Improve situation where new landwards point on parallel profile is not within the raster grid](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L222) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L394) -- TODO 019 [Improve situation where Dean profile has a near-zero elevation difference](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L286) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L394) -- TODO 020 [Check calculation of elevation of coast point of Dean parallel profile](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L949) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L1435) -- TODO 021 [Improve situation where all layers have zero thickness](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_cliff_collapse.cpp#L819) -- TODO 025 [Improve situation where this point has only zero thickness layers](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L195) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L508) -- TODO 026 [Check situation where cell in parallel profile is not in a polygon](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L495) -- TODO 028 [Give a warning if raster input layer has several bands](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L605) -- TODO 038 [Do better error handling if insufficient memory](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L71) -- TODO 053 [Improve handling of situation where landward elevation of profile is -ve](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L1581) -- TODO 055 [Maybe add a safety check?] -- TODO 080 [Do we get -ve breaking wave heights here?] -- TODO 084 [Improve handling of situation where consecutive profile points are same distance from shoreline] - -## Theory/Efficiency -- TODO 002 [Do we really need D50 for drift landform class? What do we need for drift?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/assign_landforms.cpp#L346) -- TODO 005 [Maybe give every coast point a value for end-of-profile wave height and direction instead of for deep water wave height and direction](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L58) -- TODO 010 [Do we also need to update the active zone cells?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L1828) -- TODO 012 [Change finding of adjacent polygons, and calculation of the length of shared normals, when we make polygon seaward length determined by depth of closure](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_polygons.cpp#L504) -- TODO 013 [Change calculation (need user input?) of coastline smoothing convexity threshold](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L119) -- TODO 014 [Profile spacing, vould try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L396) -- TODO 016 [Check mass balance for recirculating unconsolidated sediment option](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_sediment_movement.cpp#L539) -- TODO 023 [Only calculate shore platform erosion if cell is in a polygon](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L51) -- TODO 024 [Should we calculate platform erosion on a profile that has hit dry land?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L124) -- TODO 044 [Implement estuaries. Before we can do this, we will need to deal with multiple coastlines. Will need to get working where profile hits another profile which belongs to a different coast object.](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L1478) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L1537) -- TODO 037 [Need more info on nFindIndex](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/interpolate.cpp#L103-L121) -- TODO 051 [Implement other ways of calculating depth of closure, see TODO 045](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/utils.cpp#L2482-L2493) -- TODO 056 [Check this please Andres] -- TODO 059 [Implement dune landform class] -- TODO 060 [Remove 'magic numbers' from code here] -- TODO 061 [Is this safety check to depth of breaking a reasonable thing to do?] -- TODO 066 [Should this be for all layers? Check] -- TODO 067 [Is this ever non-zero? Check] -- TODO 070 [Change CShore to use allocatable arrays (https://fortran-lang.org/en/learn/best_practices/allocatable_arrays/) so that the number of points in the CShore output profiles can either be a user input, or determined by e.g. the physical length of the profile] -- TODO 075 [What if bedrock protrudes above Dean profile?] -- TODO 076 [When doing parallel profiles, start from the profile which is closest to a right angle with the coast] -- TODO 077 [As traverse between the bounding profiles creating parallel profiles, gradually change the parallel profile orientation based on distance weighting of two bounding profiles] -- TODO 078 [At present, we don't allow cliff collapse onto interventions. Is this realistic? Should it change with different types of intervention?] -- TODO 089 [Why do we get patches of sediment in the sea?] - TODO 086 [Try alternatives to GDALGridCreate(), e.g. https://gts.sourceforge.net/index.html, https://doc.cgal.org/latest/Triangulation_2/index.html#Chapter_2D_Triangulations] - TODO 088 [In (almost) all whole-grid loops, immediately continue if cell is hinterland (but not when calculating cliff collapse)] - -## Output -- TODO 065 [Get GPKG output working] -- TODO 063 [Add NetCDF support, see https://trac.osgeo.org/gdal/wiki/NetCDF] -- TODO 064 [Add support for grids that are not oriented N-S and W-E, but are still rectangular (will need to add a transformation in the reading and writing process, the first to bring it to the local base and the second to save it in global coordinates)] -- TODO 031 [Get raster slice output working with multiple slices](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L1122-L1125) -- TODO 032 [Improve output scaling for DBL_NODATA situation](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L1617) -- TODO 033 [Also test and configure (e.g. by passing open() options) other vector output file formats](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_utils.cpp#L893) -- TODO 034 [Also test and configure (e.g. by passing open() options) other raster output file formats](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_utils.cpp#L1642) -- TODO 043 [When outputting profiles, how do we deal with randomness of profile spacing (since profile location is determined by curvature)?] -- TODO 052 [Improve saving of profiles and parallel profiles] -- TODO 062 [Show end-of-iteration number of cells with sediment somewhere] -- TODO 068 [Only show output in log file that is relevant to processes being simulated] -- TODO 074 [Output history of what landforms are on a particular cell or cells. User inputs cell(s), how?] -- TODO 082 [Also show m_dStartIterUnconsFineAllCells etc. in log file] - -## Completed -- TODO 003 [Make coastline curvature moving window size a user input DONE in 1.1.22] -- TODO 046 [Why is cliff collapse eroded during deposition (three size classes) no longer calculated? DONE IN 1.1.22] -- TODO 058 [Dave to check this DONE in 1.1.22] -- TODO 039 [Rewrite reading of multiple random number seeds DONE in 1.2.1, 8 Nov 2024] -- TODO 041 [Read in SWL per-timestep] -- BUG 002 [Useless output e.g. clay layers even if no clay input DONE in 1.1.21] -- BUG 003 [Use mean SWL for elevations of Dean profiles DONE in 1.2.1, 27 Nov 2024] -- BUG 004 [Don't smooth intervention coastline DONE 1.2.1, 27 Nov 2024] -- TODO 073 [If output dir does not exist, then create it (ask user first) DONE 1.2.2, 28 Nov 2024] -- TODO 047 [Where is the GDAL description for the deep water wave stations vector file? DONE 1.2.3, 2 Dec 2024] -- TODO 048 [Where is the GDAL description for the flood input locations point or vector file? DONE 1.2.3, 2 Dec 2024] -- TODO 027 [Sort out GDAL problem with raster reference units DONE 1.2.3, 2 Dec 2024] -- TODO 079 [Do sanity checking on wave and tide input DONE 1.2.3, 2 Dec 2024] -- TODO 072 [CShore crashes occasionally, is it because of -ve Z values here? DONE 1.2.3, 2 Dec 2024] -- TODO 050 [Update for recent versions of Windows DONE 1.2.3, 2 Dec 2024] -- TODO 037 [Need more info on nFindIndex() DONE 1.2.3, 2 Dec 2024] -- [Improve coast normals DONE 1.2.3, 20 Dec 2024] -- TODO 057 [Check this please Manuel DONE 1.2.4, 4 Jan 2025] -- TODO 087 [Is there a problem if profile is not long enough for user-input depth of closure? DONE 1.3.0 2 Feb 2025] - +# TO DO LIST + +089 is max (also using github permalinks to code lines) + +## Table of contents +- [Documentation]{#documentation) +- [User input](#user-input) +- [Error handling](#error-handling) +- [Theory/Efficiency](#theory-efficiency) +- [Output](#output) +- [Completed](#completed) + +## Documentation +- TODO 001 [Add more information about all classes] +- TODO 007 [Need more info re. setup and surge from Manuel] + +## User input +- TODO 000 [Should user input be split in two main files] +- TODO 011 Should this constant be a user input? + [1](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/cme.h#L600-L602) + [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.cpp#L211) + [3](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.cpp#L371) +- TODO 036 [Read in changed deep water wave values](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/init_grid.cpp#L177) +- TODO 030 [Do we also need to be able to input landform sub-categories?] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L750) +- TODO 027 [Sort out GDAL problem with raster reference units](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L537) +- TODO 022 [Get intervention update working](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_intervention.cpp#L32) +- TODO 042 [Should we have a smallest valid input for KLS in the CERC equation?] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/read_input.cpp#L2378-L2380) +- TODO 045 [Method of getting depth of closure value needs to be a user input] [code](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/simulation.h#L771) +- TODO 049 [Handle other command line parameters e.g. path to .ini file, path to datafile](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/utils.cpp#L125) +- TODO 035 [Also handle other EPSG for vector spatial reference systems](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_vector.cpp#L492) +- TODO 054 [Choose more files to omit from "usual" raster output] +- TODO 069 [Enable ability to represent intervention structures which have their foundation embedded in consolidated sediment. In other words, with the elevation of the base of the intervention structure *below* the top of all consolidated sediment layers. Will need some sanity checking of elevations] +- TODO 071 [If the user input file format is changed, write a Python script to convert from the old file format to the new] +- TODO 083 [Get all three kinds of sediment input events working correctly] + +## Error handling +- TODO 038 [Do better error handling if insufficient memory] +- TODO 004 [Improve error handling of situation where we have a valid shadow zone but cannot find a neighbouring cell which is 'under' the coastline](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_shadow_zones.cpp#L490) +- TODO 006 [Check GDALGridCreate() with only start-of-coast or an end-of-coast profiles](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L214) +- TODO 009 [Decide what to do when we have eroded down to basement](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L214) +- TODO 017 [Extra safety check needed, make sure that each point is within valid grid](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L146) +- TODO 018 [Improve situation where new landwards point on parallel profile is not within the raster grid](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L222) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L394) +- TODO 019 [Improve situation where Dean profile has a near-zero elevation difference](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L286) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L394) +- TODO 020 [Check calculation of elevation of coast point of Dean parallel profile](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L949) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_within_polygon.cpp#L1435) +- TODO 021 [Improve situation where all layers have zero thickness](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_cliff_collapse.cpp#L819) +- TODO 025 [Improve situation where this point has only zero thickness layers](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L195) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L508) +- TODO 026 [Check situation where cell in parallel profile is not in a polygon](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L495) +- TODO 028 [Give a warning if raster input layer has several bands](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L605) +- TODO 038 [Do better error handling if insufficient memory](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L71) +- TODO 053 [Improve handling of situation where landward elevation of profile is -ve](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L1581) +- TODO 055 [Maybe add a safety check?] +- TODO 080 [Do we get -ve breaking wave heights here?] +- TODO 084 [Improve handling of situation where consecutive profile points are same distance from shoreline] + +## Theory/Efficiency +- TODO 002 [Do we really need D50 for drift landform class? What do we need for drift?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/assign_landforms.cpp#L346) +- TODO 005 [Maybe give every coast point a value for end-of-profile wave height and direction instead of for deep water wave height and direction](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L58) +- TODO 010 [Do we also need to update the active zone cells?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/calc_waves.cpp#L1828) +- TODO 012 [Change finding of adjacent polygons, and calculation of the length of shared normals, when we make polygon seaward length determined by depth of closure](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_polygons.cpp#L504) +- TODO 013 [Change calculation (need user input?) of coastline smoothing convexity threshold](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L119) +- TODO 014 [Profile spacing, vould try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L396) +- TODO 016 [Check mass balance for recirculating unconsolidated sediment option](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_beach_sediment_movement.cpp#L539) +- TODO 023 [Only calculate shore platform erosion if cell is in a polygon](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L51) +- TODO 024 [Should we calculate platform erosion on a profile that has hit dry land?](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/do_shore_platform_erosion.cpp#L124) +- TODO 044 [Implement estuaries. Before we can do this, we will need to deal with multiple coastlines. Will need to get working where profile hits another profile which belongs to a different coast object.](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L1478) [2](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/create_profiles.cpp#L1537) +- TODO 037 [Need more info on nFindIndex](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/interpolate.cpp#L103-L121) +- TODO 051 [Implement other ways of calculating depth of closure, see TODO 045](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/utils.cpp#L2482-L2493) +- TODO 056 [Check this please Andres] +- TODO 059 [Implement dune landform class] +- TODO 060 [Remove 'magic numbers' from code here] +- TODO 061 [Is this safety check to depth of breaking a reasonable thing to do?] +- TODO 066 [Should this be for all layers? Check] +- TODO 067 [Is this ever non-zero? Check] +- TODO 070 [Change CShore to use allocatable arrays (https://fortran-lang.org/en/learn/best_practices/allocatable_arrays/) so that the number of points in the CShore output profiles can either be a user input, or determined by e.g. the physical length of the profile] +- TODO 075 [What if bedrock protrudes above Dean profile?] +- TODO 076 [When doing parallel profiles, start from the profile which is closest to a right angle with the coast] +- TODO 077 [As traverse between the bounding profiles creating parallel profiles, gradually change the parallel profile orientation based on distance weighting of two bounding profiles] +- TODO 078 [At present, we don't allow cliff collapse onto interventions. Is this realistic? Should it change with different types of intervention?] +- TODO 089 [Why do we get patches of sediment in the sea?] + TODO 086 [Try alternatives to GDALGridCreate(), e.g. https://gts.sourceforge.net/index.html, https://doc.cgal.org/latest/Triangulation_2/index.html#Chapter_2D_Triangulations] + TODO 088 [In (almost) all whole-grid loops, immediately continue if cell is hinterland (but not when calculating cliff collapse)] + +## Output +- TODO 065 [Get GPKG output working] +- TODO 063 [Add NetCDF support, see https://trac.osgeo.org/gdal/wiki/NetCDF] +- TODO 064 [Add support for grids that are not oriented N-S and W-E, but are still rectangular (will need to add a transformation in the reading and writing process, the first to bring it to the local base and the second to save it in global coordinates)] +- TODO 031 [Get raster slice output working with multiple slices](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L1122-L1125) +- TODO 032 [Improve output scaling for DBL_NODATA situation](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_raster.cpp#L1617) +- TODO 033 [Also test and configure (e.g. by passing open() options) other vector output file formats](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_utils.cpp#L893) +- TODO 034 [Also test and configure (e.g. by passing open() options) other raster output file formats](https://github.com/coastalme/coastalme/blob/730a0be2274de02125681ab3e14424dbbfadbaeb/src/gis_utils.cpp#L1642) +- TODO 043 [When outputting profiles, how do we deal with randomness of profile spacing (since profile location is determined by curvature)?] +- TODO 052 [Improve saving of profiles and parallel profiles] +- TODO 062 [Show end-of-iteration number of cells with sediment somewhere] +- TODO 068 [Only show output in log file that is relevant to processes being simulated] +- TODO 074 [Output history of what landforms are on a particular cell or cells. User inputs cell(s), how?] +- TODO 082 [Also show m_dStartIterUnconsFineAllCells etc. in log file] + +## Completed +- TODO 003 [Make coastline curvature moving window size a user input DONE in 1.1.22] +- TODO 046 [Why is cliff collapse eroded during deposition (three size classes) no longer calculated? DONE IN 1.1.22] +- TODO 058 [Dave to check this DONE in 1.1.22] +- TODO 039 [Rewrite reading of multiple random number seeds DONE in 1.2.1, 8 Nov 2024] +- TODO 041 [Read in SWL per-timestep] +- BUG 002 [Useless output e.g. clay layers even if no clay input DONE in 1.1.21] +- BUG 003 [Use mean SWL for elevations of Dean profiles DONE in 1.2.1, 27 Nov 2024] +- BUG 004 [Don't smooth intervention coastline DONE 1.2.1, 27 Nov 2024] +- TODO 073 [If output dir does not exist, then create it (ask user first) DONE 1.2.2, 28 Nov 2024] +- TODO 047 [Where is the GDAL description for the deep water wave stations vector file? DONE 1.2.3, 2 Dec 2024] +- TODO 048 [Where is the GDAL description for the flood input locations point or vector file? DONE 1.2.3, 2 Dec 2024] +- TODO 027 [Sort out GDAL problem with raster reference units DONE 1.2.3, 2 Dec 2024] +- TODO 079 [Do sanity checking on wave and tide input DONE 1.2.3, 2 Dec 2024] +- TODO 072 [CShore crashes occasionally, is it because of -ve Z values here? DONE 1.2.3, 2 Dec 2024] +- TODO 050 [Update for recent versions of Windows DONE 1.2.3, 2 Dec 2024] +- TODO 037 [Need more info on nFindIndex() DONE 1.2.3, 2 Dec 2024] +- [Improve coast normals DONE 1.2.3, 20 Dec 2024] +- TODO 057 [Check this please Manuel DONE 1.2.4, 4 Jan 2025] +- TODO 087 [Is there a problem if profile is not long enough for user-input depth of closure? DONE 1.3.0 2 Feb 2025] + diff --git a/cme.ini b/cme.ini index 61dcba4db..4060e515f 100644 --- a/cme.ini +++ b/cme.ini @@ -2,7 +2,7 @@ ; ; Initialization file for CoastalME ; -; Copyright (C) 2024 David Favis-Mortlock and Andres Payo +; Copyright (C) 2025 David Favis-Mortlock and Andres Payo ; ;===================================================================================================================== ; @@ -17,10 +17,11 @@ ; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; ;====================================================================================================================== -Input data file (path and name) : /Users/wilfchun/Documents/GitHub/CoastalME/CoastalME_data_local/CSE/Thorpness/in/thorpness.yaml +Input data file (path and name) : in/test_suite/minimal_cons_wave_angle_230/minimal.dat ;====================================================================================================================== -Path for output :/Users/wilfchun/Documents/GitHub/CoastalME/CoastalME_data_local/CSE/Thorpness/out/ + +Path for output : out/test_suite/minimal_cons_wave_angle_230/ ;====================================================================================================================== diff --git a/src/cell.h b/src/cell.h index d7139ec4a..9ce3c31e3 100644 --- a/src/cell.h +++ b/src/cell.h @@ -1,438 +1,438 @@ -/*! - \class CGeomCell - \brief Geometry class for the cell objects which comprise the raster grid - \details TODO 001 This is a more detailed description of the CGeomCell class. - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License - \file cell.h - \brief Contains CGeomCell definitions - -*/ - -#ifndef CELL_H -#define CELL_H -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include -using std::vector; - -#include "cell_landform.h" -#include "cell_layer.h" -#include "cme.h" -#include "raster_grid.h" - -class CGeomRasterGrid; // Forward declaration - -class CGeomCell -{ - friend class CSimulation; - - private: - //! Switch to indicate if this is a sea cell, contiguous with other sea cells - bool m_bInContiguousSea; - - //! Switch to indicate that this cell is in the contiguous runup flood area - bool m_bInContiguousFlood; - - //! Switch to indicate that this cell is in the active zone - bool m_bIsInActiveZone; - - //! Is this cell a cliff toe? - bool m_bCliffToe; - - //! Switch to indicate that this cell is 'under' a runup flood line TODO 007 Finish surge and runup stuff - bool m_bFloodLine; - - //! Switch to indicate that this cell is 'under' a runup wave flood line TODO 007 Finish surge and runup stuff - bool m_bWaveFlood; - - // //! TODO 007 Finish surge and runup stuff - // bool m_bCheckCell; - - //! TODO 007 Finish surge and runup stuff - bool m_bCheckFloodCell; - - //! Switch to show this cell is 'under' a shadow boundary - bool m_bShadowBoundary; - - //! Switch to show that this cell could be the start of a coastline - bool m_bPossibleCoastStartCell; - - //! TODO 007 Finish surge and runup stuff - bool m_bPossibleFloodStartCell; - - //! TODO 007 Finish surge and runup stuff - bool m_bFloodBySetupSurge; - - //! TODO 007 Finish surge and runup stuff - bool m_bFloodBySetupSurgeRunup; - - //! If this cell is an edge (or bounding box) cell, this specifies the edge - int m_nBoundingBoxEdge; - - //! If this cell is 'under' a coastline, this is the ID number of the coast - int m_nCoastlineID; - - //! If this cell is 'under' a coast-normal profile, this is the ID number of the profile - int m_nProfileID; - - //! If this cell is 'under' a coast-normal profile, this is the ID number of the profile's coast - int m_nProfileCoastID; - - //! If this cell is within a polygon, this is the ID of the polygon - int m_nPolygonID; - - //! If this cell is within a polygon, this is the ID number of the polygon's coast - int m_nPolygonCoastID; - - //! If this cell is 'under' a coastline normal, this is the number of the normal - int m_nCoastlineNormal; - - //! If this cell is within a shadow zone, this is the ID number of the shadow zone - int m_nShadowZoneNumber; - - //! If this cell is within a downdrift zone, this is the ID number of the downdrift zone - int m_nDownDriftZoneNumber; - - //! Used in erosion calculations, stored here for display purposes - double m_dLocalConsSlope; - - //! Elevation of basement surface (m) - double m_dBasementElevation; - - //! Slope at this cell (degrees or unitless) - double m_dSlopeForCliffToe; - - //! Depth of still water (m), is zero if not inundated - double m_dSeaDepth; - - //! Total depth of still water (m) since beginning of simulation (used to calc average) - double m_dTotSeaDepth; - - //! Wave height (m) - double m_dWaveHeight; - - //! Total wave height (m) (used to calc average) - double m_dTotWaveHeight; - - //! Wave orientation - double m_dWaveAngle; - - //! Wave period (s) - double m_dWavePeriod; - - //! Total wave orientation (used to calc average) - double m_dTotWaveAngle; - - //! Wave height if this is a deep water cell - double m_dDeepWaterWaveHeight; - - //! Wave orientation if this is a deep water cell - double m_dDeepWaterWaveAngle; - - //! Wave period if this is a deep water cell - double m_dDeepWaterWavePeriod; - - //! Only meaningful if in zone of platform erosion. 0 = fully protected; 1 = ! no protection - double m_dBeachProtectionFactor; - - //! Suspended sediment as depth equivalent (m) - double m_dSuspendedSediment; - - //! Total depth of suspended sediment (m) since simulation start (used to calc average) - double m_dTotSuspendedSediment; - - //! Depth of sediment on the shore platform that could be eroded this timestep, if no supply-limitation - double m_dPotentialPlatformErosionThisIter; - - //! Total depth of sediment eroded from the shore platform, if no supply-limitation - double m_dTotPotentialPlatformErosion; - - //! Depth of sediment actually eroded from the shore platform this timestep - double m_dActualPlatformErosionThisIter; - - //! Total depth of sediment actually eroded from the shore platform - double m_dTotActualPlatformErosion; - - //! Depth of fine sediment (consolidated and unconsolidated) removed via cliff collapse this timestep - double m_dCliffCollapseFineThisIter; - - //! Depth of sand sediment (consolidated and unconsolidated) removed via cliff collapse this timestep - double m_dCliffCollapseSandThisIter; - - //! Depth of coarse sediment (consolidated and unconsolidated) removed via cliff collapse this timestep - double m_dCliffCollapseCoarseThisIter; - - //! Total depth of fine sediment (consolidated and unconsolidated) removed via cliff collapse - double m_dTotFineCliffCollapse; - - //! Total depth of sand sediment (consolidated and unconsolidated) removed via cliff collapse - double m_dTotSandCliffCollapse; - - //! Total depth of coarse sediment (consolidated and unconsolidated) removed via cliff collapse - double m_dTotCoarseCliffCollapse; - - //! Depth of unconsolidated sand sediment deposited as a result of cliff collapse this timestep - double m_dTalusSandDepositionThisIter; - - //! Total depth of unconsolidated sand sediment deposited as a result of cliff collapse - double m_dTotTalusSandDeposition; - - //! Depth of unconsolidated coarse sediment deposited as a result of cliff collapse this timestep - double m_dTalusCoarseDepositionThisIter; - - //! Total depth of unconsolidated coarse sediment deposited as a result of cliff collapse - double m_dTotTalusCoarseDeposition; - - //! Depth of sand-sized cliff collapse talus moved to unconsolidated sediment this timestep - double m_dSandTalusToUnconsThisIter; - - //! Total depth of sand-sized cliff collapse talus moved to unconsolidated sediment - double m_dTotSandTalusToUncons; - - //! Depth of coarse-sized cliff collapse talus moved to unconsolidated sediment this timestep - double m_dCoarseTalusToUnconsThisIter; - - //! Total depth of coarse-sized cliff collapse talus moved to unconsolidated sediment - double m_dTotCoarseTalusToUncons; - - //! Depth of unconsolidated beach sediment that could be eroded this timestep, if no supply-limitation - double m_dPotentialBeachErosionThisIter; - - //! Total depth of unconsolidated beach sediment eroded; if no supply-limitation - double m_dTotPotentialBeachErosion; - - //! Depth of unconsolidated beach sediment actually eroded this timestep - double m_dActualBeachErosionThisIter; - - //! Total depth of unconsolidated beach sediment actually eroded - double m_dTotActualBeachErosion; - - //! Depth of unconsolidated beach sediment deposited this timestep - double m_dBeachDepositionThisIter; - - //! Total depth of unconsolidated beach sediment deposited - double m_dTotBeachDeposition; - - //! d50 of unconsolidated sediment on top layer with unconsolidated sediment depth > 0 - double m_dUnconsD50; - - //! Height of intervention structure - double m_dInterventionHeight; - - //! This cell's landform data - CRWCellLandform m_Landform; - - // Initialize these as empty vectors - - //! Cell sediment layers NOT including the basement. Layer 0 is the lowest - vector m_VLayerAboveBasement; - - //! Number of layer-top elevations (inc. that of the basement, which is m_VdAllHorizonTopElev[0]) size 1 greater than size of m_VLayerAboveBasement - vector m_VdAllHorizonTopElev; - - protected: - public: - static CGeomRasterGrid *m_pGrid; - - CGeomCell(); - ~CGeomCell(void); - - void SetInContiguousSea(void); - bool bIsInContiguousSea(void) const; - - void SetInContiguousFlood(void); - void UnSetInContiguousFlood(void); - void SetFloodBySetupSurge(void); - bool bIsFloodBySetupSurge(void) const; - void SetFloodBySetupSurgeRunup(void); - bool bIsFloodBySetupSurgeRunup(void) const; - bool bIsInContiguousSeaFlood(void) const; - - void SetInActiveZone(bool const); - bool bIsInActiveZone(void) const; - bool bPotentialPlatformErosion(void) const; - bool bActualPlatformErosion(void) const; - void SetAsCoastline(int const); - bool bIsCoastline(void) const; - int nGetCoastline(void) const; - void SetAsFloodline(bool const); - bool bIsFloodline(void) const; - - void SetAsCliffToe(bool const); - bool bIsCliffToe(void) const; - - int nGetProfileID(void) const; - bool bIsProfile(void) const; - int nGetProfileCoastID(void) const; - void SetCoastAndProfileID(int const, int const); - - void SetShadowZoneBoundary(void); - bool bIsShadowZoneBoundary(void) const; - - void SetBoundingBoxEdge(int const); - int nGetBoundingBoxEdge(void) const; - bool bIsBoundingBoxEdge(void) const; - - void SetPossibleCoastStartCell(void); - bool bIsPossibleCoastStartCell(void) const; - - void SetPossibleFloodStartCell(void); - bool bIsPossibleFloodStartCell(void) const; - - int nGetPolygonID(void) const; - int nGetPolygonCoastID(void) const; - void SetCoastAndPolygonID(int const, int const); - - CRWCellLandform* pGetLandform(void); - - void SetWaveFlood(void); - bool bElevLessThanSWL(void) const; - - void SetCheckCell(void); - bool bIsCellCheck(void) const; - - void SetCheckFloodCell(void); - void UnSetCheckFloodCell(void); - bool bIsCellFloodCheck(void) const; - - void SetLocalConsSlope(double const); - double dGetConsSedSlope(void) const; - - void SetSlopeForCliffToe(double const); - double dGetSlopeForCliffToe(void) const; - - bool bIsInundated(void); - double dGetThisIterSWL(void) const; - double dGetThisIterTotWaterLevel(void) const; // Not used TODO 007 Finish surge and runup stuff - void SetSeaDepth(void); - double dGetSeaDepth(void) const; - void InitCell(void); - double dGetTotSeaDepth(void) const; - - void SetWaveHeight(double const); - double dGetWaveHeight(void) const; - double dGetTotWaveHeight(void) const; - void SetWaveAngle(double const); - double dGetWaveAngle(void) const; - double dGetTotWaveAngle(void) const; - - void SetCellDeepWaterWaveHeight(double const); - double dGetCellDeepWaterWaveHeight(void) const; - void SetCellDeepWaterWaveAngle(double const); - double dGetCellDeepWaterWaveAngle(void) const; - void SetCellDeepWaterWavePeriod(double const); - double dGetCellDeepWaterWavePeriod(void) const; - - void SetWaveValuesToDeepWaterWaveValues(void); - - void SetBeachProtectionFactor(double const); - double dGetBeachProtectionFactor(void) const; - - void SetSuspendedSediment(double const); - void AddSuspendedSediment(double const); - double dGetSuspendedSediment(void) const; - double dGetTotSuspendedSediment(void) const; - - int nGetTopNonZeroLayerAboveBasement(void) const; - int nGetNumOfTopLayerAboveBasement(void) const; - - int nGetNumLayers(void) const; - double dGetConsSedTopElevForLayerAboveBasement(int const) const; - CRWCellLayer* pGetLayerAboveBasement(int const); - void AppendLayers(int const); - void CalcAllLayerElevsAndD50(void); - int nGetLayerAtElev(double const) const; - double dCalcLayerElev(const int); - - double dGetConsFineDepthAllLayers(void) const; - double dGetUnconsFineDepthAllLayers(void) const; - double dGetConsSandDepthAllLayers(void) const; - double dGetUnconsSandDepthAllLayers(void) const; - double dGetConsCoarseDepthAllLayers(void) const; - double dGetUnconsCoarseDepthAllLayers(void) const; - - double dGetAllConsDepthAllLayers(void) const; - double dGetAllUnconsDepthAllLayers(void) const; - double dGetAllSedDepthAllLayers(void) const; - - double dGetTalusDepth(void) const; - - void SetBasementElev(double const); - double dGetBasementElev(void) const; - bool bBasementElevIsMissingValue(void) const; - double dGetAllSedTopElevOmitTalus(void) const; - double dGetAllSedTopElevIncTalus(void); - double dGetTopElevIncSea(void); - double dGetConsSedTopElevOmitTalus(void) const; - double dGetConsSedTopElevIncTalus(void); - - void SetPotentialPlatformErosion(double const); - double dGetPotentialPlatformErosion(void) const; - double dGetTotPotentialPlatformErosion(void) const; - - void SetActualPlatformErosion(double const); - double dGetActualPlatformErosion(void) const; - double dGetTotActualPlatformErosion(void) const; - - void IncrCliffCollapseErosion(double const, double const, double const); - double dGetThisIterCliffCollapseErosionFine(void) const; - double dGetThisIterCliffCollapseErosionSand(void) const; - double dGetThisIterCliffCollapseErosionCoarse(void) const; - double dGetTotCliffCollapseFine(void) const; - double dGetTotCliffCollapseSand(void) const; - double dGetTotCliffCollapseCoarse(void) const; - - void AddSandTalusDeposition(double const); - double dGetThisIterCliffCollapseSandTalusDeposition(void) const; - double dGetTotSandTalusDeposition(void) const; - void AddCoarseTalusDeposition(double const); - double dGetThisIterCliffCollapseCoarseTalusDeposition(void) const; - double dGetTotCoarseTalusDeposition(void) const; - - void AddSandTalusToUncons(double const); - double dGetThisIterSandTalusToUncons(void); - double dGetTotSandTalusToUncons(void); - void AddCoarseTalusToUncons(double const); - double dGetThisIterCoarseTalusToUncons(void); - double dGetTotCoarseTalusToUncons(void); - - void SetPotentialBeachErosion(double const); - double dGetPotentialBeachErosion(void) const; - double dGetTotPotentialBeachErosion(void) const; - void SetActualBeachErosion(double const); - double dGetActualBeachErosion(void) const; - double dGetTotActualBeachErosion(void) const; - // bool bActualBeachErosionThisIter(void) const; - - void IncrBeachDeposition(double const); - double dGetBeachDeposition(void) const; - double dGetTotBeachDeposition(void) const; - // bool bBeachDepositionThisIter(void) const; - - bool bBeachErosionOrDepositionThisIter(void) const; - - double dGetUnconsD50(void) const; - - int nGetInterventionClass(void) const; - void SetInterventionHeight(double const); - double dGetInterventionHeight(void) const; - double dGetInterventionTopElev(void) const; - - void SetShadowZoneNumber(int const); - int nGetShadowZoneNumber(void) const; - bool bIsinThisShadowZone(int const) const; - bool bIsinAnyShadowZone(void) const; - void SetDownDriftZoneNumber(int const); - int nGetDownDriftZoneNumber(void) const; -}; -#endif // CELL_H +/*! + \class CGeomCell + \brief Geometry class for the cell objects which comprise the raster grid + \details TODO 001 This is a more detailed description of the CGeomCell class. + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License + \file cell.h + \brief Contains CGeomCell definitions + +*/ + +#ifndef CELL_H +#define CELL_H +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include +using std::vector; + +#include "cell_landform.h" +#include "cell_layer.h" +#include "cme.h" +#include "raster_grid.h" + +class CGeomRasterGrid; // Forward declaration + +class CGeomCell +{ + friend class CSimulation; + + private: + //! Switch to indicate if this is a sea cell, contiguous with other sea cells + bool m_bInContiguousSea; + + //! Switch to indicate that this cell is in the contiguous runup flood area + bool m_bInContiguousFlood; + + //! Switch to indicate that this cell is in the active zone + bool m_bIsInActiveZone; + + //! Is this cell a cliff toe? + bool m_bCliffToe; + + //! Switch to indicate that this cell is 'under' a runup flood line TODO 007 Finish surge and runup stuff + bool m_bFloodLine; + + //! Switch to indicate that this cell is 'under' a runup wave flood line TODO 007 Finish surge and runup stuff + bool m_bWaveFlood; + + // //! TODO 007 Finish surge and runup stuff + // bool m_bCheckCell; + + //! TODO 007 Finish surge and runup stuff + bool m_bCheckFloodCell; + + //! Switch to show this cell is 'under' a shadow boundary + bool m_bShadowBoundary; + + //! Switch to show that this cell could be the start of a coastline + bool m_bPossibleCoastStartCell; + + //! TODO 007 Finish surge and runup stuff + bool m_bPossibleFloodStartCell; + + //! TODO 007 Finish surge and runup stuff + bool m_bFloodBySetupSurge; + + //! TODO 007 Finish surge and runup stuff + bool m_bFloodBySetupSurgeRunup; + + //! If this cell is an edge (or bounding box) cell, this specifies the edge + int m_nBoundingBoxEdge; + + //! If this cell is 'under' a coastline, this is the ID number of the coast + int m_nCoastlineID; + + //! If this cell is 'under' a coast-normal profile, this is the ID number of the profile + int m_nProfileID; + + //! If this cell is 'under' a coast-normal profile, this is the ID number of the profile's coast + int m_nProfileCoastID; + + //! If this cell is within a polygon, this is the ID of the polygon + int m_nPolygonID; + + //! If this cell is within a polygon, this is the ID number of the polygon's coast + int m_nPolygonCoastID; + + //! If this cell is 'under' a coastline normal, this is the number of the normal + int m_nCoastlineNormal; + + //! If this cell is within a shadow zone, this is the ID number of the shadow zone + int m_nShadowZoneNumber; + + //! If this cell is within a downdrift zone, this is the ID number of the downdrift zone + int m_nDownDriftZoneNumber; + + //! Used in erosion calculations, stored here for display purposes + double m_dLocalConsSlope; + + //! Elevation of basement surface (m) + double m_dBasementElevation; + + //! Slope at this cell (degrees or unitless) + double m_dSlopeForCliffToe; + + //! Depth of still water (m), is zero if not inundated + double m_dSeaDepth; + + //! Total depth of still water (m) since beginning of simulation (used to calc average) + double m_dTotSeaDepth; + + //! Wave height (m) + double m_dWaveHeight; + + //! Total wave height (m) (used to calc average) + double m_dTotWaveHeight; + + //! Wave orientation + double m_dWaveAngle; + + //! Wave period (s) + double m_dWavePeriod; + + //! Total wave orientation (used to calc average) + double m_dTotWaveAngle; + + //! Wave height if this is a deep water cell + double m_dDeepWaterWaveHeight; + + //! Wave orientation if this is a deep water cell + double m_dDeepWaterWaveAngle; + + //! Wave period if this is a deep water cell + double m_dDeepWaterWavePeriod; + + //! Only meaningful if in zone of platform erosion. 0 = fully protected; 1 = ! no protection + double m_dBeachProtectionFactor; + + //! Suspended sediment as depth equivalent (m) + double m_dSuspendedSediment; + + //! Total depth of suspended sediment (m) since simulation start (used to calc average) + double m_dTotSuspendedSediment; + + //! Depth of sediment on the shore platform that could be eroded this timestep, if no supply-limitation + double m_dPotentialPlatformErosionThisIter; + + //! Total depth of sediment eroded from the shore platform, if no supply-limitation + double m_dTotPotentialPlatformErosion; + + //! Depth of sediment actually eroded from the shore platform this timestep + double m_dActualPlatformErosionThisIter; + + //! Total depth of sediment actually eroded from the shore platform + double m_dTotActualPlatformErosion; + + //! Depth of fine sediment (consolidated and unconsolidated) removed via cliff collapse this timestep + double m_dCliffCollapseFineThisIter; + + //! Depth of sand sediment (consolidated and unconsolidated) removed via cliff collapse this timestep + double m_dCliffCollapseSandThisIter; + + //! Depth of coarse sediment (consolidated and unconsolidated) removed via cliff collapse this timestep + double m_dCliffCollapseCoarseThisIter; + + //! Total depth of fine sediment (consolidated and unconsolidated) removed via cliff collapse + double m_dTotFineCliffCollapse; + + //! Total depth of sand sediment (consolidated and unconsolidated) removed via cliff collapse + double m_dTotSandCliffCollapse; + + //! Total depth of coarse sediment (consolidated and unconsolidated) removed via cliff collapse + double m_dTotCoarseCliffCollapse; + + //! Depth of unconsolidated sand sediment deposited as a result of cliff collapse this timestep + double m_dTalusSandDepositionThisIter; + + //! Total depth of unconsolidated sand sediment deposited as a result of cliff collapse + double m_dTotTalusSandDeposition; + + //! Depth of unconsolidated coarse sediment deposited as a result of cliff collapse this timestep + double m_dTalusCoarseDepositionThisIter; + + //! Total depth of unconsolidated coarse sediment deposited as a result of cliff collapse + double m_dTotTalusCoarseDeposition; + + //! Depth of sand-sized cliff collapse talus moved to unconsolidated sediment this timestep + double m_dSandTalusToUnconsThisIter; + + //! Total depth of sand-sized cliff collapse talus moved to unconsolidated sediment + double m_dTotSandTalusToUncons; + + //! Depth of coarse-sized cliff collapse talus moved to unconsolidated sediment this timestep + double m_dCoarseTalusToUnconsThisIter; + + //! Total depth of coarse-sized cliff collapse talus moved to unconsolidated sediment + double m_dTotCoarseTalusToUncons; + + //! Depth of unconsolidated beach sediment that could be eroded this timestep, if no supply-limitation + double m_dPotentialBeachErosionThisIter; + + //! Total depth of unconsolidated beach sediment eroded; if no supply-limitation + double m_dTotPotentialBeachErosion; + + //! Depth of unconsolidated beach sediment actually eroded this timestep + double m_dActualBeachErosionThisIter; + + //! Total depth of unconsolidated beach sediment actually eroded + double m_dTotActualBeachErosion; + + //! Depth of unconsolidated beach sediment deposited this timestep + double m_dBeachDepositionThisIter; + + //! Total depth of unconsolidated beach sediment deposited + double m_dTotBeachDeposition; + + //! d50 of unconsolidated sediment on top layer with unconsolidated sediment depth > 0 + double m_dUnconsD50; + + //! Height of intervention structure + double m_dInterventionHeight; + + //! This cell's landform data + CRWCellLandform m_Landform; + + // Initialize these as empty vectors + + //! Cell sediment layers NOT including the basement. Layer 0 is the lowest + vector m_VLayerAboveBasement; + + //! Number of layer-top elevations (inc. that of the basement, which is m_VdAllHorizonTopElev[0]) size 1 greater than size of m_VLayerAboveBasement + vector m_VdAllHorizonTopElev; + + protected: + public: + static CGeomRasterGrid *m_pGrid; + + CGeomCell(); + ~CGeomCell(void); + + void SetInContiguousSea(void); + bool bIsInContiguousSea(void) const; + + void SetInContiguousFlood(void); + void UnSetInContiguousFlood(void); + void SetFloodBySetupSurge(void); + bool bIsFloodBySetupSurge(void) const; + void SetFloodBySetupSurgeRunup(void); + bool bIsFloodBySetupSurgeRunup(void) const; + bool bIsInContiguousSeaFlood(void) const; + + void SetInActiveZone(bool const); + bool bIsInActiveZone(void) const; + bool bPotentialPlatformErosion(void) const; + bool bActualPlatformErosion(void) const; + void SetAsCoastline(int const); + bool bIsCoastline(void) const; + int nGetCoastline(void) const; + void SetAsFloodline(bool const); + bool bIsFloodline(void) const; + + void SetAsCliffToe(bool const); + bool bIsCliffToe(void) const; + + int nGetProfileID(void) const; + bool bIsProfile(void) const; + int nGetProfileCoastID(void) const; + void SetCoastAndProfileID(int const, int const); + + void SetShadowZoneBoundary(void); + bool bIsShadowZoneBoundary(void) const; + + void SetBoundingBoxEdge(int const); + int nGetBoundingBoxEdge(void) const; + bool bIsBoundingBoxEdge(void) const; + + void SetPossibleCoastStartCell(void); + bool bIsPossibleCoastStartCell(void) const; + + void SetPossibleFloodStartCell(void); + bool bIsPossibleFloodStartCell(void) const; + + int nGetPolygonID(void) const; + int nGetPolygonCoastID(void) const; + void SetCoastAndPolygonID(int const, int const); + + CRWCellLandform* pGetLandform(void); + + void SetWaveFlood(void); + bool bElevLessThanSWL(void) const; + + void SetCheckCell(void); + bool bIsCellCheck(void) const; + + void SetCheckFloodCell(void); + void UnSetCheckFloodCell(void); + bool bIsCellFloodCheck(void) const; + + void SetLocalConsSlope(double const); + double dGetConsSedSlope(void) const; + + void SetSlopeForCliffToe(double const); + double dGetSlopeForCliffToe(void) const; + + bool bIsInundated(void); + double dGetThisIterSWL(void) const; + double dGetThisIterTotWaterLevel(void) const; // Not used TODO 007 Finish surge and runup stuff + void SetSeaDepth(void); + double dGetSeaDepth(void) const; + void InitCell(void); + double dGetTotSeaDepth(void) const; + + void SetWaveHeight(double const); + double dGetWaveHeight(void) const; + double dGetTotWaveHeight(void) const; + void SetWaveAngle(double const); + double dGetWaveAngle(void) const; + double dGetTotWaveAngle(void) const; + + void SetCellDeepWaterWaveHeight(double const); + double dGetCellDeepWaterWaveHeight(void) const; + void SetCellDeepWaterWaveAngle(double const); + double dGetCellDeepWaterWaveAngle(void) const; + void SetCellDeepWaterWavePeriod(double const); + double dGetCellDeepWaterWavePeriod(void) const; + + void SetWaveValuesToDeepWaterWaveValues(void); + + void SetBeachProtectionFactor(double const); + double dGetBeachProtectionFactor(void) const; + + void SetSuspendedSediment(double const); + void AddSuspendedSediment(double const); + double dGetSuspendedSediment(void) const; + double dGetTotSuspendedSediment(void) const; + + int nGetTopNonZeroLayerAboveBasement(void) const; + int nGetNumOfTopLayerAboveBasement(void) const; + + int nGetNumLayers(void) const; + double dGetConsSedTopElevForLayerAboveBasement(int const) const; + CRWCellLayer* pGetLayerAboveBasement(int const); + void AppendLayers(int const); + void CalcAllLayerElevsAndD50(void); + int nGetLayerAtElev(double const) const; + double dCalcLayerElev(const int); + + double dGetConsFineDepthAllLayers(void) const; + double dGetUnconsFineDepthAllLayers(void) const; + double dGetConsSandDepthAllLayers(void) const; + double dGetUnconsSandDepthAllLayers(void) const; + double dGetConsCoarseDepthAllLayers(void) const; + double dGetUnconsCoarseDepthAllLayers(void) const; + + double dGetAllConsDepthAllLayers(void) const; + double dGetAllUnconsDepthAllLayers(void) const; + double dGetAllSedDepthAllLayers(void) const; + + double dGetTalusDepth(void) const; + + void SetBasementElev(double const); + double dGetBasementElev(void) const; + bool bBasementElevIsMissingValue(void) const; + double dGetAllSedTopElevOmitTalus(void) const; + double dGetAllSedTopElevIncTalus(void); + double dGetTopElevIncSea(void); + double dGetConsSedTopElevOmitTalus(void) const; + double dGetConsSedTopElevIncTalus(void); + + void SetPotentialPlatformErosion(double const); + double dGetPotentialPlatformErosion(void) const; + double dGetTotPotentialPlatformErosion(void) const; + + void SetActualPlatformErosion(double const); + double dGetActualPlatformErosion(void) const; + double dGetTotActualPlatformErosion(void) const; + + void IncrCliffCollapseErosion(double const, double const, double const); + double dGetThisIterCliffCollapseErosionFine(void) const; + double dGetThisIterCliffCollapseErosionSand(void) const; + double dGetThisIterCliffCollapseErosionCoarse(void) const; + double dGetTotCliffCollapseFine(void) const; + double dGetTotCliffCollapseSand(void) const; + double dGetTotCliffCollapseCoarse(void) const; + + void AddSandTalusDeposition(double const); + double dGetThisIterCliffCollapseSandTalusDeposition(void) const; + double dGetTotSandTalusDeposition(void) const; + void AddCoarseTalusDeposition(double const); + double dGetThisIterCliffCollapseCoarseTalusDeposition(void) const; + double dGetTotCoarseTalusDeposition(void) const; + + void AddSandTalusToUncons(double const); + double dGetThisIterSandTalusToUncons(void); + double dGetTotSandTalusToUncons(void); + void AddCoarseTalusToUncons(double const); + double dGetThisIterCoarseTalusToUncons(void); + double dGetTotCoarseTalusToUncons(void); + + void SetPotentialBeachErosion(double const); + double dGetPotentialBeachErosion(void) const; + double dGetTotPotentialBeachErosion(void) const; + void SetActualBeachErosion(double const); + double dGetActualBeachErosion(void) const; + double dGetTotActualBeachErosion(void) const; + // bool bActualBeachErosionThisIter(void) const; + + void IncrBeachDeposition(double const); + double dGetBeachDeposition(void) const; + double dGetTotBeachDeposition(void) const; + // bool bBeachDepositionThisIter(void) const; + + bool bBeachErosionOrDepositionThisIter(void) const; + + double dGetUnconsD50(void) const; + + int nGetInterventionClass(void) const; + void SetInterventionHeight(double const); + double dGetInterventionHeight(void) const; + double dGetInterventionTopElev(void) const; + + void SetShadowZoneNumber(int const); + int nGetShadowZoneNumber(void) const; + bool bIsinThisShadowZone(int const) const; + bool bIsinAnyShadowZone(void) const; + void SetDownDriftZoneNumber(int const); + int nGetDownDriftZoneNumber(void) const; +}; +#endif // CELL_H diff --git a/src/cme.cpp b/src/cme.cpp index 52b6b3086..8c87462e2 100644 --- a/src/cme.cpp +++ b/src/cme.cpp @@ -1,52 +1,52 @@ -/*! - \file cme.cpp - \brief The start-up routine for CoastalME - \details TODO 001 A more detailed description of this routine - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include - -#include "simulation.h" - -// #include // Include this to check for first appearance in NaN when debugging (comment out, otherwise) - -//=============================================================================================================================== -//! CoastalME's main function -//=============================================================================================================================== -int main(int argc, char const* argv[]) -{ - // This is to check for first appearance of NaN when debugging (comment out, otherwise) - // #ifdef __APPLE__ - // #else - // feenableexcept(FE_INVALID | FE_OVERFLOW); - // #endif - - // Enable the use of UTF-8 symbols in CoastalME output - setlocale(LC_ALL, "en_GB.UTF-8"); - - // Create a CSimulation object - CSimulation* pSimulation = new CSimulation; - - // Run the simulation and then check how it ends - int const nRtn = pSimulation->nDoSimulation(argc, argv); - pSimulation->DoSimulationEnd(nRtn); - - // Get rid of the CSimulation object and close files - delete pSimulation; - - // Go back to the OS - return nRtn; -} +/*! + \file cme.cpp + \brief The start-up routine for CoastalME + \details TODO 001 A more detailed description of this routine + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include + +#include "simulation.h" + +// #include // Include this to check for first appearance in NaN when debugging (comment out, otherwise) + +//=============================================================================================================================== +//! CoastalME's main function +//=============================================================================================================================== +int main(int argc, char const* argv[]) +{ + // This is to check for first appearance of NaN when debugging (comment out, otherwise) + // #ifdef __APPLE__ + // #else + // feenableexcept(FE_INVALID | FE_OVERFLOW); + // #endif + + // Enable the use of UTF-8 symbols in CoastalME output + setlocale(LC_ALL, "en_GB.UTF-8"); + + // Create a CSimulation object + CSimulation* pSimulation = new CSimulation; + + // Run the simulation and then check how it ends + int const nRtn = pSimulation->nDoSimulation(argc, argv); + pSimulation->DoSimulationEnd(nRtn); + + // Get rid of the CSimulation object and close files + delete pSimulation; + + // Go back to the OS + return nRtn; +} diff --git a/src/cme.h b/src/cme.h index 7a01354ce..dfab0178a 100644 --- a/src/cme.h +++ b/src/cme.h @@ -1,1276 +1,1276 @@ -/*! - \mainpage - \section intro_sec Introduction - - CoastalME (Coastal Modelling Environment) simulates the long-term behaviour of a coast. This initial version considers only simple soft cliff cross-shore effects. However, development of CoastalME is ongoing. Watch this space!\n\n - - CoastalME was devised and constructed by Andres Payo Garcia (British Geological Survey: agarcia@bgs.ac.uk) and David Favis-Mortlock (British Geological Survey: dfm1@bgs.ac.uk). We are very grateful to the following for support, assistance, and inspiration: Tom Ashby, Manuel Cobos Budia, Wilf Chun, Mark Dickson, Jim W. Hall, Martin D. Hurst, Matthew Ives, Robert J. Nicholls, Ian Townend, and Mike - J.A. Walkden.\n\n - - See https://github.com/coastalme/coastalme for the stable release version, and the unstable development version, of the source code.\n - \n - From Shingle Street\n - To Orford Ness\n - The waves maraud,\n - The winds oppress,\n - The earth can’t help\n - But acquiesce\n - For this is east\n - And east means loss,\n - A lessening shore, receding ground,\n - Three feet gone last year, four feet this\n - Where land runs out and nothing’s sound.\n - Nothing lasts long on Shingle Street.\n - \n - By Blake Morrison (2018). See https://www.penguin.co.uk/books/419911/shingle-street-by-morrison-blake/9780701188771\n - - \section install_sec Installing CoastalME - - \subsection install_step1 Obtaining the source code - - CoastalME builds easily using Linux. If you wish to run CoastalME on Windows, then we currently recommend using the Windows Subsystem Linux (WSL) software to do this. - - Create a local copy of the github repository, for example by downloading a zipfile, then unpacking it or cloning. We suggest unpacking it to something like "/home/YOUR NAME/Projects/CoastalME/", this is then your CoastalME folder. - - git clone https://github.com/coastalme/coastalme - - \subsection install_step2 Building CoastalME - - In a terminal window (i.e. at a command-line prompt) move to the CoastalME folder. - - Then move to the the src folder - - cd CoastalME/src - - and run run_cmake.sh - - ./run_cmake.sh - - If you get a "Permission denied" message: -bash: ./run_cmake.sh: Permission denied you will have to grant executable permission using chmod a+x run_cmake.sh, chmod a+x cshore/make_cshore.sh and then - ./run_cmake.sh - - This will build CShore, look for GDAL, and write the CMake files. If you see error messages about missing software (for example, telling you that CMake cannot be found or is too old, or GDAL cannot be found or is too old) then you need to install or update the software that is causing the problem. - - Next, run - - make install - - This will create an executable file called cme in the CoastalME folder. - - \section run_sec Running CoastalME - - \subsection run_step1 Specifying the input file - - Edit cme.ini to tell CoastalME which input file to read (for example, in/test_suite/minimal_wave_angle_230/minimal.dat). - - \subsection run_step2 Running CoastalME - - Leave the src folder, and run cme - - cd .. - ./cme - - Output will appear in the "Path for output" folder. - - \subsection run_step3 Running CoastalME's test suite - - To check that your installation is running correctly, you can run a suite of pre-defined tests by running the following commands: - - chmod a+x run_test_suite.sh - ./run_test_suite.sh - - The `chmod` comand ensures that you have permission to execute the run_test_suite.sh file. - - \subsection run_step4 Managing CoastalME's output - - Once you have CoastalME (CME) up and running, you can reduce the quantity of output (it can be overwhelming!) in several ways. - - Change "Content of log file" in the CME input file for any of the test suite runs (the name of this input file is listed in cme.ini, both are simple text files). If you set "Content of log file" to zero, then CME won't output a log file; setting it to 4 (all output) is really only useful to developers. - - Change "GIS vector files to output" and "GIS vector files to output" in the CME input file. These are both set to "all" in the test suite files on GitHub. Instead of "all" you can list the space-separated codes for only the GIS output that you want to see. A list of CME GIS output codes is in codes.txt. - - Enjoy! - - \file cme.h - \brief This file contains global definitions for CoastalME -*/ - -/* - NOTE Before releasing a new version, do a pre-release build to check for memory leaks with -fsanitize options enabled (see CMakeLists.txt) then run ./cme 2> sanitize.txt NOT UNDER DEBUG (i.e. not using gdb) - - TODOLIST - *********************************************************************************************************** - DOCUMENTATION - TODO 001 Add more Doxygen information about all classes - TODO 007 We now have setup and surge info from CShore (thanks to Manuel). But what shall we do with this info? "The variable VdWaveSetupSurge() represents the sea level rise due to wave effects (setup) and storm surge. CSHORE calculates them together and they can’t be separated. That’s what the VdWaveSetupSurge variable is. That’s why you saw my initial efforts to try to separate both variables from CSHORE commented out, which is impossible. What is possible is to get the RunUp from CSHORE, but since it uses an empirical formula for that, I finally decided to calculate it separately. To your question about whether you should remove VdStormSurge, the answer is yes. I left it because I still intend at some point to extract the cross-shore transport from CSHORE and balance it in CME with the longshore and cross-shore transports without needing the Dean profile. From my point of view, this would be even more realistic, though at first it will surely drive us crazy." - - USER INPUT - TODO 000 Should user input be split in two main files: one for frequently-changed things, one for rarely-changed things? If so, what should go into each file ('testing only' OK, but what else?) - TODO 011 Should this constant be a user input? If so, TODO 071 - TODO 036 Read in changed deep water wave values (need TODO 071) - TODO 022 Get intervention update working (need TODO 071) - TODO 042 Should we have a smallest valid input for KLS in the CERC equation? - TODO 045 Method of getting depth of closure value needs to be a user input (need TODO 071) - TODO 049 Handle other command line parameters e.g. path to .ini file, path to datafile - TODO 035 Also handle other EPSG for vector spatial reference systems - TODO 054 Choose more files to omit from "usual" raster output - TODO 069 Enable ability to represent intervention structures which have their foundation embedded in consolidated sediment. In other words, with the elevation of the base of the intervention structure *below* the top of all consolidated sediment layers. Will need some sanity checking of elevations - TODO 071 If the user input file format is changed, write a Python script to convert from the old file format to the new - TODO 083 Get all three kinds of sediment input events working correctly - - ERROR HANDLING - TODO 038 Do better error handling if insufficient memory - TODO 004 Improve error handling of situation where we have a valid shadow zone but cannot find a neighbouring cell which is 'under' the coastline - TODO 006 Check GDALGridCreate() with only start-of-coast or an end-of-coast profiles - TODO 009 Decide what to do when we have eroded down to basement - TODO 017 Extra safety check needed, make sure that each point is within valid grid - TODO 018 Improve situation where new landwards point on parallel profile is not within the raster grid - TODO 019 Improve situation where Dean profile has a near-zero elevation difference - TODO 020 Check calculation of elevation of coast point of Dean parallel profile - TODO 021 Improve situation where all layers have zero thickness - TODO 025 Improve situation where this point has only zero thickness layers - TODO 026 Check situation where cell in parallel profile is not in a polygon - TODO 028 Give a warning if raster input layer has several bands - TODO 053 Improve handling of situation where landward elevation of profile is -ve - TODO 055 Maybe add a safety check here? - TODO 080 Do we get -ve breaking wave heights here? - TODO 084 Improve handling of situation where consecutive profile points are same distance from shoreline - - THEORY/EFFICIENCY - TODO 002 Do we really need D50 for drift landform class? What do we need for drift? - TODO 005 Maybe give every coast point a value for end-of-profile wave height and direction instead of for deep water wave height and direction - TODO 010 Do we also need to update the active zone cells? - TODO 012 Change finding of adjacent polygons, and calculation of the length of shared normals, when we make polygon seaward length determined by depth of closure - TODO 013 Change calculation (need user input?) of coastline smoothing convexity threshold - TODO 014 Profile spacing, could try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity - TODO 016 Check mass balance for recirculating unconsolidated sediment option - TODO 023 Only calculate shore platform erosion if cell is in a polygon - TODO 024 Should we calculate platform erosion on a profile that has hit dry land? - TODO 044 Implement estuaries - TODO 051 Implement other ways of calculating depth of closure, see TODO 045 - TODO 056 Check this please Andres - TODO 059 Implement dune landform class - TODO 060 Remove 'magic numbers' from code here - TODO 061 Is this safety check to depth of breaking a reasonable thing to do? - TODO 066 Should this be for all layers? Check - TODO 067 Suspended fine sediment never decreases i.e. no suspended fine sediment ever leaves the grid. Is this OK? - TODO 070 Change CShore to use allocatable arrays (https://fortran-lang.org/en/learn/best_practices/allocatable_arrays/) so that the number of points in the CShore output profiles can either be a user input, or determined by e.g. the physical length of the profile. At present, max is NN = 1000 in cshore_wrapper.f03 - TODO 075 What if bedrock sticks above Dean profile? - TODO 076 When doing parallel profiles, start from the profile which is closest to a right angle with the coast - TODO 077 As traverse between the bounding profiles creating parallel profiles, gradually change the parallel profile orientation based on distance weighting of two bounding profiles - TODO 078 At present, we don't allow cliff collapse onto interventions. Is this realistic? Should it be different for different types on intervention? - TODO 089 Why do we get patches of sediment in the sea? - TODO 086 Try these as a more efficient replacement for GDALGridCreate(): https://github.com/delfrrr/delaunator-cpp https://www.cs.cmu.edu/~quake/triangle.html https://github.com/greenm01/poly2tri https://gts.sourceforge.net/index.html - TODO 088 In (almost) all whole-grid loops, immediately continue if cell is hinterland (but not when calculating cliff collapse) - TODO 090 At present, sediment cannot move from a given coastline polygon to a polygon belonging to another coastline. Is this always true? - TODO 092 If we have only fine sediment, the surface formed as the coast recedes inland is dead level (because fine sediment goes to suspension, and hence the Dean profile stuff does not operate). This causes problems with profile creation and CShore. Need to impose a small slope here somehow - TODO 093 There are a number of cell attributes that are really only useful for debugging. To keep memory usage down on release versions, need to flag these attributes and their methods so that they are included only in debug versions - TODO 094 Problems with sediment recirculation when large volumes of sedimentare introduced at the input end of the coast: need to spread this input sediment over the whole of the grid-end polygon - TODO 095 Parallel profiles seem to create "streaks" of above-water sediment, these streaks interfere with subsequent profile creation (profiles which hit steaks arte marked as invalid). Investigate this - TODO 096 It is OK to specify OGRFieldDefn objects as const in recent versions (e.g. 14.2) of g++, but in older versions of g++ (e.g. 12.2) have problems with const here. Maybe make the const a pre-processor condion? - - OUTPUT - TODO 065 Get GPKG output working: GDAL 3.9.1 does not yet implement this correctly. Currently is OK for vector output (but is very slow), not yet working for raster output - TODO 063 Add NetCDF support, see https://trac.osgeo.org/gdal/wiki/NetCDF - TODO 064 Add support for grids that are not oriented N-S and W-E, but which are still rectangular. See https://gdal.org/en/stable/tutorials/geotransforms_tut.html - TODO 031 Get raster slice output working with multiple slices - TODO 032 Improve output scaling for DBL_NODATA situation - TODO 033 Also test and configure (e.g. by passing open() options) other vector output file formats - TODO 034 Also test and configure (e.g. by passing open() options) other raster output file formats - TODO 043 When outputting profiles, how do we deal with randomness of profile spacing (since profile location is determined by curvature)? - TODO 052 Improve saving of profiles and parallel profiles - TODO 062 Show end-of-iteration number of cells with sediment somewhere - TODO 068 Only show output in log file that is relevant to processes being simulated - TODO 074 Output history of what landforms are on a particular cell or cells. User inputs cell(s), how? - TODO 082 Also show m_dStartIterUnconsFineAllCells etc. in log file - - 096 is max - - COMPLETED - TODO 003 Make coastline curvature moving window size a user input DONE in 1.1.22 - TODO 046 Why is cliff collapse eroded during deposition (three size classes) no longer calculated? DONE IN 1.1.22 - TODO 058 Dave to check this DONE in 1.1.22 - TODO 039 Rewrite reading of multiple random number seeds DONE in 1.2.1, 8 Nov 2024 - TODO 041 Read in SWL per-timestep - BUG 002 Useless output e.g. clay layers even if no clay input DONE in 1.1.21 - BUG 003 Use mean SWL for elevations of Dean profiles DONE in 1.2.1, 27 Nov 2024 - BUG 004 Don't smooth intervention coastline DONE 1.2.1, 27 Nov 2024 - TODO 073 If output dir does not exist, then create it (ask user first) DONE 1.2.2, 28 Nov 2024 - TODO 047 Where is the GDAL description for the deep water wave stations vector file? DONE 1.2.3, 2 Dec 2024 - TODO 048 Where is the GDAL description for the flood input locations point or vector file? DONE 1.2.3, 2 Dec 2024 - TODO 027 Sort out GDAL problem with raster reference units DONE 1.2.3, 2 Dec 2024 - TODO 079 Do sanity checking on wave and tide input DONE 1.2.3, 2 Dec 2024 - TODO 072 CShore crashes occasionally, is it because of -ve Z values here? DONE 1.2.3, 2 Dec 2024 - TODO 050 Update for recent versions of Windows DONE 1.2.3, 2 Dec 2024 - TODO 037 Need more info on nFindIndex() DONE 1.2.3, 2 Dec 2024 Improve coast normals DONE 1.2.3, 20 Dec 2024 - TODO 057 Check this please Manuel DONE 1.2.4, 4 Jan 2025 - TODO 087 Is there a problem if profile is not long enough for user-input depth of closure? DONE 1.3.0 2 Feb 2025 -*/ - -#ifndef CME_H -#define CME_H -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include - -#include -using std::string; - -#include -using std::ostringstream; - -#include -using std::ostream; - -#include - -//===================================================== platform-specific stuff ================================================= -#ifdef _WIN32 - #define access _access - #define F_OK 0 // Test for file existence -#endif - -#ifdef _MSC_VER - // MS Visual C++ compiler, byte order is IEEE little-endian - #ifdef _DEBUG - #include // useful - #endif - - // clock_t is a signed long: see - long const CLOCK_T_MIN = LONG_MIN; - double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); - - #ifdef _M_ALPHA - string const PLATFORM = "MS Visual C++ for Alpha"; - #elif defined _M_IX86 - string const PLATFORM = "MS Visual C++ for Intel x86"; - #elif defined _M_MPPC - string const PLATFORM = "MS Visual C++ for Power PC"; - #elif defined _M_MRX000 - string const PLATFORM = "MS Visual C++ for MIPS"; - #else - string const PLATFORM = "MS Visual C++ for unknown CPU"; - #endif - -#elif defined __GNUG__ - // GNU compiler - #ifndef CPU - #error "CPU not defined" - #else - #ifdef x86 - // Intel x86, byte order is little-endian - string const PLATFORM = "GNU Compiler for Intel x86"; - // clock_t is an unsigned long: see - unsigned long const CLOCK_T_MIN = 0; - double const CLOCK_T_RANGE = static_cast(ULONG_MAX); - #elif defined rs6000 - // IBM RS-6000, byte order is big-endian - string const PLATFORM = "GNU complier for IBM RS-6000"; - // clock_t is a signed long: see NEED TO CHECK - long const CLOCK_T_MIN = LONG_MIN; - double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); - #elif defined ultrasparc - // Sun UltraSparc, byte order is big-endian - string const PLATFORM = "GNU compiler for Sun UltraSPARC"; - // clock_t is a signed long: see - long const CLOCK_T_MIN = LONG_MIN; - double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); - #else - // Something else - string const PLATFORM = "GNU compiler for unknown CPU"; - // clock_t is a signed long: NEED TO CHECK - long const CLOCK_T_MIN = LONG_MIN; - double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); - #endif - #endif - -#elif defined __clang__ - // Clang compiler - #ifndef CPU - #error "CPU not defined" - #else - #ifdef x86 - // Intel x86, byte order is little-endian - string const PLATFORM = "Clang compiler for Intel x86"; - // clock_t is an unsigned long: see - unsigned long const CLOCK_T_MIN = 0; - double const CLOCK_T_RANGE = static_cast(ULONG_MAX); - #else - // Something else - string const PLATFORM = "Clang compiler for unknown CPU"; - // clock_t is a signed long: NEED TO CHECK - long const CLOCK_T_MIN = LONG_MIN; - double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); - #endif - #endif - -#elif defined __MINGW32__ - // Minimalist GNU for Windows - // #define __USE_MINGW_ANSI_STDIO 1 // Fix long doubles output problem, - // see http://stackoverflow.com/questions/7134547/gcc-printf-and-long-double-leads-to-wrong-output-c-type-conversion-messes-u - #define WEXITSTATUS(x) ((x) & 0xff) - -#elif defined __HP_aCC - // HP-UX aCC, byte order is big-endian, can be either 32-bit or 64-bit - string const PLATFORM = "HP-UX aC++"; - // clock_t is an unsigned long: see - unsigned long const CLOCK_T_MIN = 0; - #ifdef __ia64 - // However, clock_t is a 32-bit unsigned long and we are using 64-bit unsigned longs here - double const CLOCK_T_RANGE = 4294967295UL; // crude, improve - #else - double const CLOCK_T_RANGE = static_cast(ULONG_MAX); - #endif -#endif - -// TODO: Check -// #if defined(WIN32) -// #define STRCASECMP(a, b) (_stricmp(a, b)) -// #define STRNCASECMP(a, b, n) (_strnicmp(a, b, n)) -// #else -// /** Alias for strcasecmp() */ -// #define STRCASECMP(a, b) (strcasecmp(a, b)) -// /** Alias for strncasecmp() */ -// #define STRNCASECMP(a, b, n) (strncasecmp(a, b, n)) -// //# endif -// /** Alias for strncasecmp() == 0 */ -// #define EQUALN(a, b, n) (STRNCASECMP(a, b, n) == 0) -// /** Alias for strcasecmp() == 0 */ -// #define EQUAL(a, b) (STRCASECMP(a, b) == 0) -// #endif - -//===================================================== hard-wired constants ==================================================== -char const COLON = ':'; -char const COMMA = ','; -char const DASH = '-'; -char const PATH_SEPARATOR = '/'; // Works for Windows too! -char const QUOTE1 = ';'; -char const QUOTE2 = '#'; -char const SLASH = '/'; -char const SPACE = ' '; -char const TILDE = '~'; - -// TESTING options -bool const ACCEPT_TRUNCATED_PROFILES = true; -bool const CREATE_SHADOW_ZONE_IF_HITS_GRID_EDGE = true; // If shadow line tracing hits grid edge, create shadow zone? -bool const SAVE_CSHORE_OUTPUT = true; // #ifdef CSHORE_FILE_INOUT || CSHORE_BOTH, append all CShore output files to a whole-run master -bool const USE_DEEP_WATER_FOR_SHADOW_LINE = true; // Use deep water wave orientation in determining shadow line orientation? - -// Not likely that user will need to change these -int const NUMBER_OF_RNGS = 2; // Number of random number generators -int const SAVEMAX = 100000; // Maximum number of saves of spatial output -int const BUF_SIZE = 2048; // Max length (inc. terminating NULL) of any C-type string -int const CAPE_POINT_MIN_SPACING = 10; // In cells: for shadow zone stuff, cape points must not be closer than this -int const CLOCK_CHECK_ITERATION = 5000; // If have done this many timesteps then reset the CPU time running total -int const COAST_LENGTH_MAX = 100; // For safety check when tracing coast -int const COAST_LENGTH_MIN_X_PROF_SPACE = 20; // Ignore very short coasts less than this x profile spacing - -//! The size of the arrays output by CShore. If this is changed, then must also set the same value on line 12 of cshore_wrapper.f03 (integer, parameter :: NN = 1000, NL = 1) and recompile CShore. Eventually we should move to dynamically allocated arrays TODO 070 -int const CSHOREARRAYOUTSIZE = 1000; - -int const FLOOD_FILL_START_OFFSET = 2; // In cells: cell-by-cell fill starts this distance inside polygon -int const GRID_MARGIN = 10; // Ignore this many along-coast grid-edge points re. shadow zone calcs -int const INT_NODATA = -9999; // CME's internal NODATA value for ints -int const MAX_LEN_SHADOW_LINE_TO_IGNORE = 200; // In cells: if can't find cell-by-cell fill start point, continue if short shadow line -int const MAX_NUM_PREV_ORIENTATION_VALUES = 10; // Max length of deque used in tracing shadow boundary -int const MAX_NUM_SHADOW_ZONES = 10; // Consider at most this number of shadow zones -int const MIN_INLAND_OFFSET_UNCONS_EROSION = 5; // Used in estimation of beach erosion -int const MIN_PARALLEL_PROFILE_SIZE = 3; // In cells: min size for valid unconsolidated sediment parallel profile -int const MIN_PROFILE_SIZE = 3; // In cells: min size for valid unconsolidated sediment profile -int const DEFAULT_PROFILE_SPACING = 15; // In cells: profile creation does not work well if profiles are too closely spaced -int const SAVGOL_POLYNOMIAL_MAX_ORDER = 6; // Maximum order of Savitzky-Golay smoothing polynomial - -// Log file detail level -int const NO_LOG_FILE = 0; -int const LOG_FILE_LOW_DETAIL = 1; -int const LOG_FILE_MIDDLE_DETAIL = 2; -int const LOG_FILE_HIGH_DETAIL = 3; -int const LOG_FILE_ALL = 4; - -// Direction codes -int const NO_DIRECTION = 0; -int const NORTH = 1; -int const NORTH_EAST = 2; -int const EAST = 3; -int const SOUTH_EAST = 4; -int const SOUTH = 5; -int const SOUTH_WEST = 6; -int const WEST = 7; -int const NORTH_WEST = 8; - -int const DIRECTION_DOWNCOAST = 0; // Down-coast, i.e. along the coast so that the index of coastline points INCREASES -int const DIRECTION_UPCOAST = 1; // Up-coast, i.e. along the coast so that the index of coastline points DECREASES - -// Handedness codes, these show which side the sea is on when travelling down-coast (i.e. in the direction in which coastline point numbers INCREASE) -int const NULL_HANDED = -1; -int const RIGHT_HANDED = 0; -int const LEFT_HANDED = 1; - -// Sediment texture codes -int const TEXTURE_FINE = 0; -int const TEXTURE_SAND = 1; -int const TEXTURE_COARSE = 2; - -// Time unit codes -int const TIME_UNKNOWN = -1; -int const TIME_HOURS = 0; -int const TIME_DAYS = 1; -int const TIME_MONTHS = 2; -int const TIME_YEARS = 3; - -// Intervention input and output codes -int const IO_INTERVENTION_NONE = 0; -int const IO_INTERVENTION_STRUCT = 1; -int const IO_INTERVENTION_NON_STRUCT = 2; - -// Landform category codes -int const LF_UNKNOWN = 0; -int const LF_HINTERLAND = 1; -int const LF_SEA = 2; -int const LF_CLIFF_ON_COASTLINE = 6; -int const LF_CLIFF_INLAND = 7; -int const LF_DRIFT_TALUS = 9; -int const LF_DRIFT_BEACH = 10; -int const LF_DRIFT_DUNES = 11; // TODO 059 Implement dune landform class -int const LF_INTERVENTION_STRUCT = 12; -int const LF_INTERVENTION_NON_STRUCT = 13; -int const LF_ISLAND = 14; // Not yet implemented -int const LF_SEDIMENT_INPUT_UNCONSOLIDATED = 18; -int const LF_SEDIMENT_INPUT_CONSOLIDATED = 19; - -// GIS raster input codes -int const FINE_CONS_RASTER = 1; -int const SAND_CONS_RASTER = 2; -int const COARSE_CONS_RASTER = 3; -int const FINE_UNCONS_RASTER = 4; -int const SAND_UNCONS_RASTER = 5; -int const COARSE_UNCONS_RASTER = 6; -int const SUSP_SED_RASTER = 7; -int const LANDFORM_RASTER = 8; -int const INTERVENTION_CLASS_RASTER = 9; -int const INTERVENTION_HEIGHT_RASTER = 10; - -// GIS vector data type codes -int const VEC_FIELD_DATA_ANY = 0; -int const VEC_FIELD_DATA_INT = 1; -int const VEC_FIELD_DATA_REAL = 2; -int const VEC_FIELD_DATA_STRING = 3; -int const VEC_FIELD_DATA_OTHER = 4; - -// GIS vector geometry codes -int const VEC_GEOMETRY_POINT = 1; -int const VEC_GEOMETRY_LINE = 2; -int const VEC_GEOMETRY_POLYGON = 3; -int const VEC_GEOMETRY_OTHER = 4; - -// GIS vector input codes and constraints -int const DEEP_WATER_WAVE_STATIONS_VEC = 1; -int const DEEP_WATER_WAVE_STATIONS_MAX_LAYER = 1; -int const DEEP_WATER_WAVE_STATIONS_POINT_GEOMETRY = VEC_GEOMETRY_POINT; -int const SEDIMENT_INPUT_EVENT_LOCATION_VEC = 2; -int const SEDIMENT_INPUT_EVENT_LOCATION_MAX_LAYER = 1; -int const SEDIMENT_INPUT_EVENT_LOCATION_POINT_GEOMETRY = VEC_GEOMETRY_POINT; -int const FLOOD_LOCATION_POINT_GEOMETRY = VEC_GEOMETRY_POINT; -int const SEDIMENT_INPUT_EVENT_LOCATION_LINE_GEOMETRY = VEC_GEOMETRY_LINE; -int const FLOOD_LOCATION_VEC = 3; -int const FLOOD_LOCATION_MAX_LAYER = 1; - -// GIS raster output codes - -int const RASTER_PLOT_ACTIVE_ZONE = 1; -int const RASTER_PLOT_ACTUAL_BEACH_EROSION = 2; -int const RASTER_PLOT_ACTUAL_PLATFORM_EROSION = 3; -int const RASTER_PLOT_AVG_SEA_DEPTH = 4; -int const RASTER_PLOT_AVG_SUSPENDED_SEDIMENT = 5; -int const RASTER_PLOT_AVG_WAVE_HEIGHT = 6; -int const RASTER_PLOT_AVG_WAVE_ORIENTATION = 7; -int const RASTER_PLOT_BASEMENT_ELEVATION = 8; -int const RASTER_PLOT_BEACH_DEPOSITION = 9; -int const RASTER_PLOT_BEACH_MASK = 10; -int const RASTER_PLOT_BEACH_PROTECTION = 11; -int const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE = 12; -int const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND = 13; -int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE = 14; -int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE = 15; -int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND = 16; -#ifdef _DEBUG -int const RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP = 17; -#endif -int const RASTER_PLOT_CLIFF_NOTCH_ALL = 18; -int const RASTER_PLOT_CLIFF_TOE = 19; -int const RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT = 20; -int const RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT = 21; -int const RASTER_PLOT_COAST = 22; -int const RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT = 23; -int const RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION = 24; -int const RASTER_PLOT_DEEP_WATER_WAVE_PERIOD = 25; -int const RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT = 26; -int const RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT = 27; -int const RASTER_PLOT_INTERVENTION_CLASS = 28; -int const RASTER_PLOT_INTERVENTION_HEIGHT = 29; -int const RASTER_PLOT_INUNDATION_MASK = 30; -int const RASTER_PLOT_LANDFORM = 31; -int const RASTER_PLOT_NORMAL_PROFILE = 32; -int const RASTER_PLOT_TOP_ELEV_INC_SEA = 33; -int const RASTER_PLOT_POLYGON = 34; -int const RASTER_PLOT_POLYGON_GAIN_OR_LOSS = 35; -int const RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT = 36; -int const RASTER_PLOT_POTENTIAL_BEACH_EROSION = 37; -int const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION = 38; -int const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK = 39; -int const RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT = 40; -int const RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT = 41; -int const RASTER_PLOT_SEA_DEPTH = 42; -int const RASTER_PLOT_SEDIMENT_INPUT = 43; -int const RASTER_PLOT_SED_TOP_INC_TALUS_ELEV = 44; -int const RASTER_PLOT_SETUP_SURGE_FLOOD_MASK = 45; -int const RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK = 46; -int const RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE = 47; -int const RASTER_PLOT_SHADOW_ZONE = 48; -int const RASTER_PLOT_SLICE = 49; -int const RASTER_PLOT_SLOPE_FOR_CLIFF_TOE = 50; -int const RASTER_PLOT_CONS_SED_SLOPE = 51; -int const RASTER_PLOT_SUSPENDED_SEDIMENT = 52; -int const RASTER_PLOT_TALUS = 53; -int const RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION = 54; -int const RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION = 55; -int const RASTER_PLOT_TOTAL_BEACH_DEPOSITION = 56; -int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE = 57; -int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND = 58; -int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE = 59; -int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE = 60; -int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND = 61; -int const RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION = 62; -int const RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION = 63; -int const RASTER_PLOT_WAVE_FLOOD_LINE = 64; -int const RASTER_PLOT_WAVE_HEIGHT = 65; -int const RASTER_PLOT_WAVE_ORIENTATION = 66; - -// GIS vector output codes -// int const VECTOR_PLOT_FLOOD_SWL_SETUP_LINE = 19; -int const VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT = 1; -int const VECTOR_PLOT_BREAKING_WAVE_HEIGHT = 2; -int const VECTOR_PLOT_CLIFF_EDGE = 3; -int const VECTOR_PLOT_CLIFF_NOTCH_ACTIVE = 4; -int const VECTOR_PLOT_COAST = 5; -int const VECTOR_PLOT_COAST_CURVATURE = 6; -int const VECTOR_PLOT_COAST_SWL_HIGHEST = 7; -int const VECTOR_PLOT_COAST_SWL_LOWEST = 8; -int const VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT = 9; -int const VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY = 10; -int const VECTOR_PLOT_FLOOD_LINE = 11; -int const VECTOR_PLOT_INVALID_NORMALS = 12; -int const VECTOR_PLOT_MEAN_WAVE_ENERGY = 13; -int const VECTOR_PLOT_NORMALS = 14; -int const VECTOR_PLOT_POLYGON_BOUNDARY = 15; -int const VECTOR_PLOT_POLYGON_NODES = 16; -int const VECTOR_PLOT_RUN_UP = 17; -int const VECTOR_PLOT_SHADOW_ZONE_BOUNDARY = 18; -int const VECTOR_PLOT_STORM_SURGE = 19; -int const VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT = 20; -int const VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE = 21; -int const VECTOR_PLOT_WAVE_SETUP = 22; -int const VECTOR_PLOT_WAVE_TRANSECT_POINTS = 23; -// int const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE = 24; -// int const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE = 25; - -// Return codes -int const RTN_OK = 0; -int const RTN_HELP_ONLY = 1; -int const RTN_CHECK_ONLY = 2; -int const RTN_USER_ABORT = 3; -int const RTN_ERR_BADPARAM = 4; -int const RTN_ERR_INI = 5; -int const RTN_ERR_CMEDIR = 6; -int const RTN_ERR_RUNDATA = 7; -int const RTN_ERR_SCAPE_SHAPE_FUNCTION_FILE = 8; -int const RTN_ERR_TIDEDATAFILE = 9; -int const RTN_ERR_LOGFILE = 10; -int const RTN_ERR_OUTFILE = 11; -int const RTN_ERR_TSFILE = 12; -int const RTN_ERR_DEMFILE = 13; -int const RTN_ERR_RASTER_FILE_READ = 14; -int const RTN_ERR_VECTOR_FILE_READ = 15; -int const RTN_ERR_MEMALLOC = 16; -int const RTN_ERR_RASTER_GIS_OUT_FORMAT = 17; -int const RTN_ERR_VECTOR_GIS_OUT_FORMAT = 18; -int const RTN_ERR_TEXT_FILE_WRITE = 19; -int const RTN_ERR_RASTER_FILE_WRITE = 20; -int const RTN_ERR_VECTOR_FILE_WRITE = 21; -int const RTN_ERR_TIMESERIES_FILE_WRITE = 22; -int const RTN_ERR_LINETOGRID = 23; -int const RTN_ERR_PROFILE_SPACING = 24; -// 25 is now missing -int const RTN_ERR_PROFILE_ENDPOINT_IS_INLAND = 26; -int const RTN_ERR_NO_SOLUTION_FOR_ENDPOINT = 27; -int const RTN_ERR_PROFILE_END_INSUFFICIENT_DEPTH = 28; -int const RTN_ERR_NO_PROFILES_1 = 29; -int const RTN_ERR_NO_PROFILES_2 = 30; -int const RTN_ERR_NOSEACELLS = 31; -int const RTN_ERR_GRID_TO_LINE = 32; -int const RTN_ERR_NO_COAST = 34; -int const RTN_ERR_PROFILE_WRITE = 35; -int const RTN_ERR_TIME_UNITS = 36; -int const RTN_ERR_CLIFF_NOTCH = 37; -int const RTN_ERR_CLIFF_CANNOT_DEPOSIT_ALL = 38; -int const RTN_ERR_BAD_INDEX = 39; -int const RTN_ERR_EDGE_OF_GRID = 40; -int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_BEACH_EROSION = 42; -int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_UPCOAST_BEACH_DEPOSITION = 43; -int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_DOWNCOAST_BEACH_DEPOSITION = 44; -int const RTN_ERR_LANDFORM_TO_GRID = 45; -int const RTN_ERR_NO_TOP_LAYER = 46; -int const RTN_ERR_NO_ADJACENT_POLYGON = 47; -int const RTN_ERR_BAD_MULTILINE = 48; -int const RTN_ERR_CANNOT_INSERT_POINT = 49; -int const RTN_ERR_CANNOT_ASSIGN_COASTAL_LANDFORM = 50; -int const RTN_ERR_SHADOW_ZONE_FLOOD_FILL_NOGRID = 51; -int const RTN_ERR_SHADOW_ZONE_FLOOD_START_POINT = 52; -int const RTN_ERR_CSHORE_EMPTY_PROFILE = 53; -int const RTN_ERR_CSHORE_FILE_INPUT = 54; -int const RTN_ERR_READING_CSHORE_FILE_OUTPUT = 55; -int const RTN_ERR_WAVE_INTERPOLATION_LOOKUP = 56; -int const RTN_ERR_GRIDCREATE = 57; -int const RTN_ERR_COAST_CANT_FIND_EDGE_CELL = 58; -int const RTN_ERR_CSHORE_ERROR = 59; -int const RTN_ERR_NO_CELL_UNDER_COASTLINE = 60; -int const RTN_ERR_OPEN_DEEP_WATER_WAVE_DATA = 61; -int const RTN_ERR_READING_DEEP_WATER_WAVE_DATA = 62; -int const RTN_ERR_BOUNDING_BOX = 63; -int const RTN_ERR_READING_SEDIMENT_INPUT_EVENT = 64; -int const RTN_ERR_SEDIMENT_INPUT_EVENT = 65; -int const RTN_ERR_SEDIMENT_INPUT_EVENT_LOCATION = 66; -int const RTN_ERR_WAVESTATION_LOCATION = 67; -int const RTN_ERR_FLOOD_LOCATION = 68; -int const RTN_ERR_CLIFF_NOT_IN_POLYGON = 69; -int const RTN_ERR_CELL_MARKED_PROFILE_COAST_BUT_NOT_PROFILE = 70; -int const RTN_ERR_TRACING_FLOOD = 71; -int const RTN_ERR_NO_START_FINISH_POINTS_TRACING_COAST = 72; -int const RTN_ERR_NO_VALID_COAST = 73; -int const RTN_ERR_REPEATING_WHEN_TRACING_COAST = 74; -int const RTN_ERR_ZERO_LENGTH_COAST = 75; -int const RTN_ERR_COAST_TOO_SMALL = 77; -int const RTN_ERR_IGNORING_COAST = 78; -int const RTN_ERR_TOO_LONG_TRACING_COAST = 79; -int const RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE_DIFFERENT_COASTS = 80; -int const RTN_ERR_POINT_NOT_FOUND_IN_MULTILINE_DIFFERENT_COASTS = 81; -int const RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE = 82; -int const RTN_ERR_CELL_IN_POLY_BUT_NO_POLY_COAST = 83; -int const RTN_ERR_CLIFF_TALUS_TO_UNCONS = 84; -int const RTN_ERR_UNKNOWN = 999; - -// Elevation and 'slice' codes -int const ELEV_IN_BASEMENT = -1; -int const ELEV_ABOVE_SEDIMENT_TOP = -2; -int const NO_NONZERO_THICKNESS_LAYERS = -3; - -// Vector smoothing codes -int const SMOOTH_NONE = 0; -int const SMOOTH_RUNNING_MEAN = 1; -int const SMOOTH_SAVITZKY_GOLAY = 2; - -// Grid-edge boundary treatment for unconsolidated sediment movement -int const GRID_EDGE_CLOSED = 0; -int const GRID_EDGE_OPEN = 1; -int const GRID_EDGE_RECIRCULATE = 2; - -// Model for wave propagation -int const WAVE_MODEL_COVE = 0; -int const WAVE_MODEL_CSHORE = 1; - -// Equation for estimating erosion of unconsolidated sediment -int const UNCONS_SEDIMENT_EQUATION_CERC = 0; -int const UNCONS_SEDIMENT_EQUATION_KAMPHUIS = 1; - -int const CLIFF_COLLAPSE_LENGTH_INCREMENT = 10; // Increment the planview length of the cliff talus Dean profile, if we have not been able to deposit enough -int const PROFILE_CHECK_DIST_FROM_COAST = 20; // TEST TODO Used in checking shoreline-normal profiles for intersection -int const GAP_BETWEEN_DIFFERENT_COAST_PROFILES = 30; // In cells, is the gap between profile ends belonging to different coasts - -int const NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC = 30; // Number of days to average daily high water elevation - -int const NO_RUNUP_EQUATION = 0; // Runup not considered -int const RUNUP_EQUATION_NIELSEN_HANSLOW = 1; // Runup equation is Nielsen, P. & Hanslow, D. J. 1991. Wave Runup Distributions on Natural Beaches. Journal of Coastal Research, 7, 1139-1152. *** & DHI (2004) ??? -int const RUNUP_EQUATION_MASE = 2; // Runup equation is Mase, H. 1989. Random Wave Runup Height on Gentle Slope. Journal of Waterway, Port, Coastal, and Ocean Engineering, 115, 649-661. -int const RUNUP_EQUATION_STOCKDON = 3; // Runup equation is Stockdon, H. F., Holman, R. A., Howd, P. A. & Sallenger JR, A. H. 2006. Empirical parameterization of setup, swash, and runup. Coastal Engineering, 53, 573-588. - -unsigned long const MASK = 0xfffffffful; -unsigned long const SEDIMENT_INPUT_EVENT_ERROR = -1; -unsigned long const UNSIGNED_LONG_NODATA = 9999; - -double const PI = 3.141592653589793238462643; - -double const D50_FINE_DEFAULT = 0.0625; // In mm -double const D50_SAND_DEFAULT = 0.42; // In mm -double const D50_COARSE_DEFAULT = 19.0; // In mm - -double const BEACH_PROTECTION_HB_RATIO = 0.23; // The beach protection factor is this times breaking depth -double const WALKDEN_HALL_PARAM_1 = 3.25; // First parameter in Equation 4 from Walkden & Hall, 2005 -double const WALKDEN_HALL_PARAM_2 = 1.50; // Second parameter in Equation 4 from Walkden & Hall, 2005 - -double const DEPTH_OVER_DB_INCREMENT = 0.001; // Depth over DB increment for erosion potential look-up function -double const INVERSE_DEPTH_OVER_DB_INCREMENT = 1000; // Inverse of the above -double const DEAN_POWER = 2.0 / 3.0; // Dean profile exponent - -// TODO 011 Let the user define these CShore input parameters -double const CSHORE_FRICTION_FACTOR = 0.015; // Friction factor for CShore model -double const CSHORE_SURGE_LEVEL = 0.0; // TODO 007 - -double const TOLERANCE = 1e-7; // For bFPIsEqual, if too small (e.g. 1e-10), get spurious "rounding" errors -double const SED_ELEV_TOLERANCE = 1e-5; // Used to compare depth-equivalent sediment amounts in bFPIsEqual and elsewhere -double const MASS_BALANCE_TOLERANCE = 1e-5; // For bFPIsEqual, used to compare for mass balance checks -double const STRAIGHT_COAST_MAX_DETAILED_CURVATURE = -5; -double const STRAIGHT_COAST_MAX_SMOOTH_CURVATURE = -1; -double const MIN_LENGTH_OF_SHADOW_ZONE_LINE = 10; // Used in shadow line tracing -double const MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE = 5; // Used in shadow line tracing -double const CLIFF_COLLAPSE_HEIGHT_INCREMENT = 0.1; // Increment the fractional height of the cliff talus Dean profile, if we have not been able to deposit enough -double const INTERVENTION_PROFILE_SPACING_FACTOR = 0.5; // Profile spacing on interventions works better if it is smaller than profile spacing on coastline - -double const CLIFF_NOTCH_CUTOFF_DISTANCE = 2; // Cut-off SWL distance (m), measured downwards from the cliff notch apex: below this there is no notch incision -double const DBL_NODATA = -9999; - -string const PROGRAM_NAME = "Coastal Modelling Environment (CoastalME) version 1.4.0 (11 Nov 2025)"; -string const PROGRAM_NAME_SHORT = "CME"; -string const CME_INI = "cme.ini"; - -string const COPYRIGHT = "(C) 2025 Andres Payo and David Favis-Mortlock"; -string const LINE = "-------------------------------------------------------------------------------"; -string const DISCLAIMER1 = "This program is distributed in the hope that it will be useful, but WITHOUT ANY"; -string const DISCLAIMER2 = "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A"; -string const DISCLAIMER3 = "PARTICULAR PURPOSE. See the GNU General Public License for more details. You"; -string const DISCLAIMER4 = "should have received a copy of the GNU General Public License along with this"; -string const DISCLAIMER5 = "program; if not, contact the Free Software Foundation, Inc., 675 Mass Ave,"; -string const DISCLAIMER6 = "Cambridge, MA 02139, USA."; - -string const ABOUT = "simulates the long-term behaviour of a coast. This initial version considers only simple soft cliff cross-shore effects"; -string const THANKS = "Many thanks to:\n\tTom Ashby\n\tManuel Cobos Budia\n\tWilf Chun\n\tMark Dickson\n\tJim W. Hall\n\tMartin D. Hurst\n\tMatthew Ives\n\tRobert J. Nicholls\n\tIan Townend\n\tMike J.A. Walkden"; -string const GDAL_DRIVERS = "GDAL drivers"; - -string const USAGE = "Usage: cme [OPTION]..."; -string const USAGE1 = " --gdal List GDAL drivers"; -string const USAGE2 = " --about Information about this program"; -string const USAGE3 = " --help Display this text"; -string const USAGE4 = " --home=DIRECTORY Specify the location of the .ini file etc."; -string const USAGE5 = " --datafile=FILE Specify the location and name of the main datafile"; -string const USAGE6 = " --yaml Use YAML format for the main datafile"; - -string const START_NOTICE = "- Started on "; -string const INITIALIZING_NOTICE = "- Initializing"; -string const READING_FILE_LOCATIONS = " - Reading file locations: "; -string const READING_RUN_DATA = " - Reading run data file: "; -string const READING_BASEMENT = " - Reading basement DEM: "; -string const READING_RASTER_FILES = " - Reading raster GIS files"; -string const READING_LANDFORM_FILE = " - Landform class: "; -string const READING_INTERVENTION_CLASS_FILE = " - Intervention class: "; -string const READING_INTERVENTION_HEIGHT_FILE = " - Intervention height: "; -string const READING_SUSPENDED_SEDIMENT_FILE = " - Suspended sediment: "; -string const READING_UNCONS_FINE_SEDIMENT_FILE = " - Unconsolidated fine sediment (layer "; -string const READING_UNCONS_SAND_SEDIMENT_FILE = " - Unconsolidated sand sediment (layer "; -string const READING_UNCONS_COARSE_SEDIMENT_FILE = " - Unconsolidated coarse sediment (layer "; -string const READING_CONS_FINE_SEDIMENT_FILE = " - Consolidated fine sediment (layer "; -string const READING_CONS_SAND_SEDIMENT_FILE = " - Consolidated sand sediment (layer "; -string const READING_CONS_COARSE_SEDIMENT_FILE = " - Consolidated coarse sediment (layer "; -string const READING_VECTOR_FILES = " - Reading vector GIS files"; -string const READING_DEEP_WATER_WAVE_FILE = " - Deep water wave values: "; -string const READING_SED_INPUT_EVENT_FILE = " - Sediment input event values: "; -string const READING_FLOOD_LOCATION = " - Characteristic locations for flood: "; -string const READING_SCAPE_SHAPE_FUNCTION_FILE = " - Reading SCAPE shape function file"; -string const READING_TIDE_DATA_FILE = " - Reading tide data file: "; -string const ALLOCATE_MEMORY = " - Allocating memory for raster grid"; -string const ADD_LAYERS = " - Adding sediment layers to raster grid"; -string const INITIALIZING_FINAL = " - Finishing initialization"; -string const RUN_NOTICE = "- Running simulation"; -string const SIMULATING = "\r - Simulating "; -string const FINAL_OUTPUT = "- Writing final output"; -string const SEND_EMAIL = " - Sending email to "; -string const RUN_END_NOTICE = "Run ended at "; -string const PRESS_KEY = "Press any key to continue..."; - -string const ERROR_NOTICE = " with error code "; -string const EMAIL_ERROR = "Could not send email"; - -string const SCAPE_DIR = "scape/"; -string const SCAPE_SHAPE_FUNCTION_FILE = "ShapeFunction.dat"; -string const EROSION_POTENTIAL_LOOKUP_FILE = "ErosionPotential.csv"; - -string const CSHORE_DIR = "cshore/"; -string const CSHORE_INFILE = "infile"; - -string const ERR = "*** ERROR "; -string const WARN = "WARNING "; -string const NOTE = " Note "; - -string const MASS_BALANCE_ERROR = "MASS BALANCE ERROR"; - -string const PER_ITER_HEAD1 = "<-----ELAPSED----><--SEA-><----POTENTIAL---><-----------ACTUAL-----------><-----POTENTIAL-----><------------ACTUAL-------------><-----------ACTUAL------------><--SEDIMENT--><---CLIFF COLLAPSE--->"; - -string const PER_ITER_HEAD2 = " TIME DEPTH PLATFORM EROSION PLATFORM EROSION BEACH EROSION BEACH EROSION BEACH DEPOSITION INPUT EVENT EROSION DEPOSITION SED"; - -string const PER_ITER_HEAD3 = "Time Hours Years Avg % Sea All Erod % Sea All Erod <--sea avg-> % Sea All Erod % Sea All Erod <--sea avg-> % Sea All Depos <--sea--> <-coast avg-><--sea-->"; - -string const PER_ITER_HEAD4 = "Step Area Sea Area Area Sea Area F S C Area Sea Area Area Sea Area F S C Area Sea Area S C F S C F S C S C F"; - -string const PER_ITER_HEAD5 = " Avg Avg Avg Avg Avg Avg Avg Avg Avg"; - -string const PER_ITER_HEAD = "PER-ITERATION RESULTS ============================================================================================================================================================================================="; - -string const PER_ITER_CSV_HEAD = "Timestep,Hours, Years, AvgSeaDepth_m, PotPlatformErosion_PctSeaArea, PotPlatformErosion_AllAvg_mm, PotPlatformErosion_ErodAvg_mm, ActPlatformErosion_PctSeaArea, ActPlatformErosion_AllAvg_mm, ActPlatformErosion_ErodAvg_mm, ActPlatformErosion_Fine_mm, ActPlatformErosion_Sand_mm, ActPlatformErosion_Coarse_mm, PotBeachErosion_PctSeaArea, PotBeachErosion_AllAvg_mm, PotBeachErosion_ErodAvg_mm, ActBeachErosion_PctSeaArea, ActBeachErosion_AllAvg_mm, ActBeachErosion_ErodAvg_mm, ActBeachErosion_Fine_mm, ActBeachErosion_Sand_mm, ActBeachErosion_Coarse_mm, BeachDeposition_PctSeaArea, BeachDeposition_AllAvg_mm, BeachDeposition_DepAvg_mm, BeachDeposition_Sand_mm, BeachDeposition_Coarse_mm, SedimentInput_Fine, SedimentInput_Sand, SedimentInput_Coarse, CliffCollapse_Fine_mm, CliffCollapse_Sand_mm, CliffCollapse_Coarse_mm, CliffDeposition_Sand_mm, CliffDeposition_Coarse_mm, SuspendedSediment_mm, GISEvents"; - -string const ENDHYDROLOGYHEAD = "END OF SIMULATION: HYDROLOGY ======================================================================================================================================================================================"; -string const ENDSEDIMENTHEAD = "END OF SIMULATION: SEDIMENT MOVEMENT =============================================================================================================================================================================="; -string const PERFORMHEAD = "END OF SIMULATION: PERFORMANCE ===================================================================================================================================================================================="; - -string const OUTEXT = ".out"; -string const LOGEXT = ".log"; -string const CSVEXT = ".csv"; - -string const DEEP_WATER_WAVE_STATION_ID = "id"; -string const SEDIMENT_INPUT_EVENT_LOCATION_ID = "id"; -string const FLOOD_LOCATION_ID = "id"; - -// GIS raster output user codes -string const RASTER_ACTIVE_ZONE_CODE = "active_zone"; -string const RASTER_ACTIVE_ZONE_NAME = "active_zone"; -string const RASTER_ACTUAL_BEACH_EROSION_CODE = "actual_beach_erosion"; -string const RASTER_ACTUAL_BEACH_EROSION_NAME = "actual_beach_erosion"; -string const RASTER_ACTUAL_PLATFORM_EROSION_CODE = "actual_platform_erosion"; -string const RASTER_ACTUAL_PLATFORM_EROSION_NAME = "actual_platform_erosion"; -string const RASTER_ALL_OUTPUT_CODE = "all"; -string const RASTER_AVG_SEA_DEPTH_CODE = "avg_sea_depth"; -string const RASTER_AVG_SEA_DEPTH_NAME = "avg_sea_depth"; -string const RASTER_AVG_SUSP_SED_CODE = "avg_susp_sed"; -string const RASTER_AVG_SUSP_SED_NAME = "avg_susp_sed"; -string const RASTER_AVG_WAVE_HEIGHT_CODE = "avg_wave_height"; -string const RASTER_AVG_WAVE_HEIGHT_NAME = "avg_wave_height"; -string const RASTER_AVG_WAVE_ORIENTATION_CODE = "avg_wave_orientation"; -string const RASTER_AVG_WAVE_ORIENTATION_NAME = "avg_wave_orientation"; -string const RASTER_BASEMENT_ELEVATION_CODE = "basement_elevation"; -string const RASTER_BASEMENT_ELEVATION_NAME = "basement_elevation"; -string const RASTER_BEACH_DEPOSITION_CODE = "beach_deposition"; -string const RASTER_BEACH_DEPOSITION_NAME = "beach_deposition"; -string const RASTER_BEACH_MASK_CODE = "beach_mask"; -string const RASTER_BEACH_MASK_NAME = "beach_mask"; -string const RASTER_BEACH_PROTECTION_CODE = "beach_protection"; -string const RASTER_BEACH_PROTECTION_NAME = "beach_protection"; -string const RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_CODE = "cliff_collapse_talus_deposition_coarse"; -string const RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME = "cliff_collapse_talus_deposition_coarse"; -string const RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_CODE = "cliff_collapse_talus_deposition_sand"; -string const RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME = "cliff_collapse_talus_deposition_sand"; -string const RASTER_CLIFF_COLLAPSE_EROSION_COARSE_CODE = "cliff_collapse_erosion_coarse"; -string const RASTER_CLIFF_COLLAPSE_EROSION_COARSE_NAME = "cliff_collapse_erosion_coarse"; -string const RASTER_CLIFF_COLLAPSE_EROSION_FINE_CODE = "cliff_collapse_erosion_fine"; -string const RASTER_CLIFF_COLLAPSE_EROSION_FINE_NAME = "cliff_collapse_erosion_fine"; -string const RASTER_CLIFF_COLLAPSE_EROSION_SAND_CODE = "cliff_collapse_erosion_sand"; -string const RASTER_CLIFF_COLLAPSE_EROSION_SAND_NAME = "cliff_collapse_erosion_sand"; -string const RASTER_CLIFF_COLLAPSE_TIMESTEP_CODE = "cliff_collapse_timestep"; -string const RASTER_CLIFF_COLLAPSE_TIMESTEP_NAME = "cliff_collapse_timestep_all"; -string const RASTER_CLIFF_NOTCH_ALL_CODE = "cliff_notch_all"; -string const RASTER_CLIFF_NOTCH_ALL_NAME = "cliff_notch_all"; -string const RASTER_CLIFF_TOE_NAME = "cliff_toe"; // Note no code for this, because is chosen by m_bCliffToeLocate in input file -string const RASTER_COARSE_CONS_CODE = "cons_sed_coarse"; -string const RASTER_COARSE_CONS_NAME = "cons_sed_coarse"; -string const RASTER_COARSE_UNCONS_CODE = "uncons_sed_coarse"; -string const RASTER_COARSE_UNCONS_NAME = "uncons_sed_coarse"; -string const RASTER_COAST_CODE = "rcoast"; -string const RASTER_COAST_NAME = "rcoast"; -string const RASTER_COAST_NORMAL_CODE = "rcoast_normal"; -string const RASTER_COAST_NORMAL_NAME = "rcoast_normal"; -string const RASTER_DEEP_WATER_WAVE_HEIGHT_CODE = "deep_water_wave_height"; -string const RASTER_DEEP_WATER_WAVE_HEIGHT_NAME = "deep_water_wave_height"; -string const RASTER_DEEP_WATER_WAVE_ORIENTATION_CODE = "deep_water_wave_orientation"; -string const RASTER_DEEP_WATER_WAVE_ORIENTATION_NAME = "deep_water_wave_orientation"; -string const RASTER_DEEP_WATER_WAVE_PERIOD_CODE = "deep_water_wave_period"; -string const RASTER_DEEP_WATER_WAVE_PERIOD_NAME = "deep_water_wave_period"; -string const RASTER_FINE_CONS_CODE = "cons_sed_fine"; -string const RASTER_FINE_CONS_NAME = "cons_sed_fine"; -string const RASTER_FINE_UNCONS_CODE = "uncons_sed_fine"; -string const RASTER_FINE_UNCONS_NAME = "uncons_sed_fine"; -string const RASTER_INTERVENTION_CLASS_CODE = "intervention_class"; -string const RASTER_INTERVENTION_CLASS_NAME = "intervention_class"; -string const RASTER_INTERVENTION_HEIGHT_CODE = "intervention_height"; -string const RASTER_INTERVENTION_HEIGHT_NAME = "intervention_height"; -string const RASTER_INUNDATION_MASK_CODE = "inundation_mask"; -string const RASTER_INUNDATION_MASK_NAME = "inundation_mask"; -string const RASTER_LANDFORM_CODE = "landform_class"; -string const RASTER_LANDFORM_NAME = "landform_class"; -string const RASTER_POLYGON_CODE = "polygon_raster"; -string const RASTER_POLYGON_GAIN_OR_LOSS_CODE = "polygon_gain_or_loss"; -string const RASTER_POLYGON_GAIN_OR_LOSS_NAME = "polygon_gain_or_loss"; -string const RASTER_POLYGON_NAME = "polygon_raster"; -string const RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_CODE = "polygon_updrift_or_downdrift"; -string const RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_NAME = "polygon_updrift_or_downdrift"; -string const RASTER_POTENTIAL_BEACH_EROSION_CODE = "potential_beach_erosion"; -string const RASTER_POTENTIAL_BEACH_EROSION_NAME = "potential_beach_erosion"; -string const RASTER_POTENTIAL_PLATFORM_EROSION_CODE = "potential_platform_erosion"; -string const RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE = "potential_platform_erosion_mask"; -string const RASTER_POTENTIAL_PLATFORM_EROSION_MASK_NAME = "potential_platform_erosion_mask"; -string const RASTER_POTENTIAL_PLATFORM_EROSION_NAME = "potential_platform_erosion"; -string const RASTER_SAND_CONS_CODE = "cons_sed_sand"; -string const RASTER_SAND_CONS_NAME = "cons_sed_sand"; -string const RASTER_SAND_UNCONS_CODE = "uncons_sed_sand"; -string const RASTER_SAND_UNCONS_NAME = "uncons_sed_sand"; -string const RASTER_SEA_DEPTH_CODE = "sea_depth"; -string const RASTER_SEA_DEPTH_NAME = "sea_depth"; -string const RASTER_SEDIMENT_INPUT_EVENT_CODE = "sediment_input_total"; -string const RASTER_SEDIMENT_INPUT_EVENT_NAME = "sediment_input_total"; -string const RASTER_SEDIMENT_TOP_CODE = "sediment_top_elevation"; -string const RASTER_SEDIMENT_TOP_ELEVATION_NAME = "sediment_top_elevation"; -string const RASTER_SETUP_SURGE_FLOOD_MASK_CODE = "flood_setup_surge_mask"; -string const RASTER_SETUP_SURGE_FLOOD_MASK_NAME = "flood_setup_surge_mask"; -string const RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_CODE = "flood_setup_surge_runup_mask"; -string const RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_NAME = "flood_setup_surge_runup_mask"; -string const RASTER_SHADOW_DOWNDRIFT_ZONE_CODE = "shadow_downdrift_zones"; -string const RASTER_SHADOW_DOWNDRIFT_ZONE_NAME = "shadow_downdrift_zones"; -string const RASTER_SHADOW_ZONE_CODE = "shadow_zones"; -string const RASTER_SHADOW_ZONE_NAME = "shadow_zones"; -string const RASTER_SLICE_CODE = "slice"; -string const RASTER_SLICE_NAME = "slice"; -string const RASTER_SLOPE_FOR_CLIFF_TOE_NAME = "toe_slope"; // Note no code for this, because is chosen by m_bCliffToeLocate in input file -string const RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_CODE = "cons_sediment_slope"; -string const RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_NAME = "cons_sediment_slope"; -string const RASTER_SUSP_SED_CODE = "susp_sed"; -string const RASTER_SUSP_SED_NAME = "susp_sed"; -string const RASTER_TALUS_CODE = "talus"; -string const RASTER_TALUS_NAME = "talus"; -string const RASTER_TOP_ELEVATION_INC_SEA_CODE = "top_elevation"; -string const RASTER_TOP_ELEVATION_INC_SEA_NAME = "top_elevation_inc_sea"; -string const RASTER_TOTAL_ACTUAL_BEACH_EROSION_CODE = "total_actual_beach_erosion"; -string const RASTER_TOTAL_ACTUAL_BEACH_EROSION_NAME = "total_actual_beach_erosion"; -string const RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_CODE = "total_actual_platform_erosion"; -string const RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_NAME = "total_actual_platform_erosion"; -string const RASTER_TOTAL_BEACH_DEPOSITION_CODE = "total_beach_deposition"; -string const RASTER_TOTAL_BEACH_DEPOSITION_NAME = "total_beach_deposition"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_CODE = "total_cliff_collapse_talus_deposition_coarse"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME = "total_cliff_collapse_talus_deposition_coarse"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_CODE = "total_cliff_collapse_talus_deposition_sand"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME = "total_cliff_collapse_talus_deposition_sand"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_CODE = "total_cliff_collapse_erosion_coarse"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_NAME = "total_cliff_collapse_erosion_coarse"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_CODE = "total_cliff_collapse_erosion_fine"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_NAME = "total_cliff_collapse_erosion_fine"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_CODE = "total_cliff_collapse_erosion_sand"; -string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_NAME = "total_cliff_collapse_erosion_sand"; -string const RASTER_TOTAL_POTENTIAL_BEACH_EROSION_CODE = "total_potential_beach_erosion"; -string const RASTER_TOTAL_POTENTIAL_BEACH_EROSION_NAME = "total_potential_beach_erosion"; -string const RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_CODE = "total_potential_platform_erosion"; -string const RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_NAME = "total_potential_platform_erosion"; -string const RASTER_USUAL_OUTPUT_CODE = "usual"; -string const RASTER_WAVE_FLOOD_LINE_CODE = "wave_flood_line"; -string const RASTER_WAVE_FLOOD_LINE_NAME = "wave_flood_line"; -string const RASTER_WAVE_HEIGHT_CODE = "wave_height"; -string const RASTER_WAVE_HEIGHT_NAME = "wave_height"; -string const RASTER_WAVE_ORIENTATION_CODE = "wave_orientation"; -string const RASTER_WAVE_ORIENTATION_NAME = "wave_orientation"; -string const RASTER_WAVE_PERIOD_CODE = "wave_period"; -string const RASTER_WAVE_PERIOD_NAME = "wave_period"; - -// GIS raster output titles -string const RASTER_PLOT_ACTIVE_ZONE_TITLE = "Active zone"; -string const RASTER_PLOT_ACTUAL_BEACH_EROSION_TITLE = "Actual (constrained) beach erosion depth"; -string const RASTER_PLOT_ACTUAL_PLATFORM_EROSION_TITLE = "Actual (constrained) shore platform erosion depth"; -string const RASTER_PLOT_AVG_SEA_DEPTH_TITLE = "Average sea depth"; -string const RASTER_PLOT_AVG_SUSPENDED_SEDIMENT_TITLE = "Average depth of suspended sediment"; -string const RASTER_PLOT_AVG_WAVE_HEIGHT_TITLE = "Average wave height"; -string const RASTER_PLOT_AVG_WAVE_ORIENTATION_TITLE = "Average wave orientation"; -string const RASTER_PLOT_BASEMENT_ELEVATION_TITLE = "Basement elevation"; -string const RASTER_PLOT_BEACH_DEPOSITION_TITLE = "Beach deposition depth"; -string const RASTER_PLOT_BEACH_MASK_TITLE = "Beach mask"; -string const RASTER_PLOT_BEACH_PROTECTION_TITLE = "Beach protection factor"; -string const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE = "Depth of coarse talus from cliff collapse"; -string const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE = "Depth of sand talus from cliff collapse"; -string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE_TITLE = "Cliff collapse depth of erosion, coarse sediment"; -string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE_TITLE = "Cliff collapse depth of erosion, fine sediment"; -string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND_TITLE = "Cliff collapse depth of erosion, sand sediment"; -#ifdef _DEBUG -string const RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP_TITLE = "Timestep at which cliff collapse occurred"; -#endif -string const RASTER_PLOT_CLIFF_NOTCH_ALL_TITLE = "All cliff notch incision"; -string const RASTER_PLOT_CLIFF_TOE_TITLE = "Cliff toe cells"; -string const RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated coarse sediment depth"; -string const RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated coarse sediment depth"; -string const RASTER_PLOT_COAST_TITLE = "Rasterized coastline"; -string const RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT_TITLE = "Deep water wave height"; -string const RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION_TITLE = "Deep water wave orientation"; -string const RASTER_PLOT_DEEP_WATER_WAVE_PERIOD_TITLE = "Deep water wave period"; -string const RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated fine sediment depth"; -string const RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated fine sediment depth"; -string const RASTER_PLOT_INTERVENTION_CLASS_TITLE = "Intervention class"; -string const RASTER_PLOT_INTERVENTION_HEIGHT_TITLE = "Intervention height"; -string const RASTER_PLOT_INUNDATION_MASK_TITLE = "Inundated area mask"; -string const RASTER_PLOT_LANDFORM_TITLE = "Landform class"; -string const RASTER_PLOT_NORMAL_PROFILE_TITLE = "Rasterized normal profiles"; -string const RASTER_PLOT_POLYGON_GAIN_OR_LOSS_TITLE = "Polygon gain or loss of unconsolidated sediment"; -string const RASTER_PLOT_POLYGON_TITLE = "Rasterized polygon boundaries"; -string const RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT_TITLE = "Polygon updrift or downdrift movement of unconsolidated sediment"; -string const RASTER_PLOT_POTENTIAL_BEACH_EROSION_TITLE = "Potential (unconstrained) beach erosion depth"; -string const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK_TITLE = "Potential (unconstrained) shore platform erosion binary mask"; -string const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_TITLE = "Potential (unconstrained) shore platform erosion depth"; -string const RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated sand sediment depth"; -string const RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated sand sediment depth"; -string const RASTER_PLOT_SEA_DEPTH_TITLE = "Sea depth"; -string const RASTER_PLOT_SEDIMENT_INPUT_EVENT_TITLE = "Sediment input event(s) since last GIS save"; -string const RASTER_PLOT_SED_TOP_INC_TALUS_ELEV_TITLE = "Elevation of sediment top inc talus"; -string const RASTER_PLOT_SETUP_SURGE_FLOOD_MASK_TITLE = "Mask of setup-surge flood"; -string const RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK_TITLE = "Mask of setup-surge-runup flood"; -string const RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE_TITLE = "Downdrift of wave shadow zones"; -string const RASTER_PLOT_SHADOW_ZONE_TITLE = "Wave shadow zones"; -string const RASTER_PLOT_SLICE_TITLE = "Slice though layers at elevation = "; -string const RASTER_PLOT_SLOPE_FOR_CLIFF_TOE_TITLE = "Slope"; -string const RASTER_PLOT_CONS_SED_SLOPE_TITLE = "Local slope of consolidated sediment"; -string const RASTER_PLOT_SUSPENDED_SEDIMENT_TITLE = "Suspended sediment depth"; -string const RASTER_PLOT_TALUS_TITLE = "Talus from cliff collapse"; -string const RASTER_PLOT_TOP_ELEV_INC_SEA_TITLE = "Topmost elevation (sediment plus intervention plus se"; -string const RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION_TITLE = "Total actual (constrained) beach erosion depth"; -string const RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION_TITLE = "Total actual (constrained) shore platform erosion depth"; -string const RASTER_PLOT_TOTAL_BEACH_DEPOSITION_TITLE = "Total beach deposition depth"; -string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE = "Total depth of coarse talus from cliff collapse"; -string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE = "Total depth of sand talus from cliff collapse"; -string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_TITLE = "Total of cliff collapse erosion depth, coarse"; -string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_TITLE = "Total of cliff collapse erosion depth, fine"; -string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_TITLE = "Total of cliff collapse erosion depth, sand"; -string const RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION_TITLE = "Total potential (unconstrained) beach erosion depth"; -string const RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION_TITLE = "Total potential (unconstrained) shore platform erosion depth"; -string const RASTER_PLOT_WAVE_FLOOD_LINE_TITLE = "Wave flood line"; -string const RASTER_PLOT_WAVE_HEIGHT_TITLE = "Wave height"; -string const RASTER_PLOT_WAVE_ORIENTATION_TITLE = "Wave orientation"; - -// GIS vector output user codes -string const VECTOR_ALL_OUTPUT_CODE = "all"; -string const VECTOR_ALL_RIVER_FLOOD_OUTPUT_CODE = "all"; -string const VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_CODE = "avg_wave_angle"; -string const VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_NAME = "avg_wave_angle"; -string const VECTOR_BREAKING_WAVE_HEIGHT_CODE = "breaking_wave_height"; -string const VECTOR_BREAKING_WAVE_HEIGHT_NAME = "breaking_wave_height"; -string const VECTOR_CLIFF_EDGE_CODE = "cliff_edge"; -string const VECTOR_CLIFF_EDGE_NAME = "cliff_edge"; -string const VECTOR_CLIFF_NOTCH_ACTIVE_CODE = "cliff_notch_active"; -string const VECTOR_CLIFF_NOTCH_ACTIVE_NAME = "cliff_notch_active"; -string const VECTOR_COAST_CODE = "coast"; -string const VECTOR_COAST_CURVATURE_CODE = "coast_curvature"; -string const VECTOR_COAST_CURVATURE_NAME = "coast_curvature"; -string const VECTOR_COAST_NAME = "coast"; -string const VECTOR_COAST_SWL_HIGHEST_NAME = "coast_SWL_highest"; -string const VECTOR_COAST_SWL_LOWEST_NAME = "coast_SWL_lowest"; -string const VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_CODE = "deep_water_wave_angle"; -string const VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_NAME = "deep_water_wave_angle"; -string const VECTOR_DOWNDRIFT_ZONE_BOUNDARY_CODE = "downdrift_boundary"; -string const VECTOR_DOWNDRIFT_ZONE_BOUNDARY_NAME = "downdrift_boundary"; -string const VECTOR_FLOOD_LINE_CODE = "flood_line"; -string const VECTOR_FLOOD_LINE_NAME = "flood_line"; -string const VECTOR_FLOOD_SWL_SETUP_LINE_CODE = "setup"; -string const VECTOR_FLOOD_SWL_SETUP_LINE_NAME = "setup"; -string const VECTOR_FLOOD_SWL_SETUP_SURGE_LINE_CODE = "setup_surge"; -string const VECTOR_FLOOD_SWL_SETUP_SURGE_LINE_NAME = "setup_surge"; -string const VECTOR_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_CODE = "setup_surge_runup"; -string const VECTOR_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_NAME = "setup_surge_runup"; -string const VECTOR_INVALID_NORMALS_CODE = "invalid_normals"; -string const VECTOR_INVALID_NORMALS_NAME = "invalid_normals"; -string const VECTOR_MEAN_WAVE_ENERGY_CODE = "mean_wave_energy"; -string const VECTOR_MEAN_WAVE_ENERGY_NAME = "mean_wave_energy"; -string const VECTOR_NORMALS_CODE = "normals"; -string const VECTOR_NORMALS_NAME = "normals"; -string const VECTOR_POLYGON_BOUNDARY_CODE = "polygon"; -string const VECTOR_POLYGON_BOUNDARY_NAME = "polygon"; -string const VECTOR_POLYGON_NODE_CODE = "polygon_node"; -string const VECTOR_POLYGON_NODE_NAME = "polygon_node"; -string const VECTOR_RUN_UP_CODE = "run_up"; -string const VECTOR_RUN_UP_NAME = "run_up"; -string const VECTOR_SHADOW_ZONE_BOUNDARY_CODE = "shadow_boundary"; -string const VECTOR_SHADOW_ZONE_BOUNDARY_NAME = "shadow_boundary"; -string const VECTOR_STORM_SURGE_CODE = "storm_surge"; -string const VECTOR_STORM_SURGE_NAME = "storm_surge"; -string const VECTOR_USUAL_OUTPUT_CODE = "usual"; -string const VECTOR_WAVE_ANGLE_AND_HEIGHT_CODE = "wave_angle"; -string const VECTOR_WAVE_ANGLE_AND_HEIGHT_NAME = "wave_angle"; -string const VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_CODE = "wave_energy"; -string const VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_NAME = "wave_energy"; -string const VECTOR_WAVE_SETUP_CODE = "wave_setup"; -string const VECTOR_WAVE_SETUP_NAME = "wave_setup"; -string const VECTOR_WAVE_TRANSECT_POINTS_CODE = "wave_transect_points"; -string const VECTOR_WAVE_TRANSECT_POINTS_NAME = "wave_transect_points"; - -// GIS vector output titles -string const VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT_TITLE = "Average wave orientation and height"; -string const VECTOR_PLOT_BREAKING_WAVE_HEIGHT_TITLE = "Breaking wave height"; -string const VECTOR_PLOT_CLIFF_EDGE_TITLE = "Cliff edge"; -string const VECTOR_PLOT_CLIFF_NOTCH_ACTIVE_TITLE = "Active cliff notch incision"; -string const VECTOR_PLOT_COAST_CURVATURE_TITLE = "Coastline curvature"; -string const VECTOR_PLOT_COAST_SWL_HIGHEST_TITLE = "Coastline at highest SWL"; -string const VECTOR_PLOT_COAST_SWL_LOWEST_TITLE = "Coastline at lowest SWL"; -string const VECTOR_PLOT_COAST_TITLE = "Coastline"; -string const VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_TITLE = "Deep water wave orientation and height"; -string const VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY_TITLE = "Downdrift zone boundary"; -string const VECTOR_PLOT_FLOOD_LINE_TITLE = "Flood "; -string const VECTOR_PLOT_FLOOD_SWL_SETUP_LINE_TITLE = "SWL-Setup line"; -string const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE_TITLE = "SWL-Setup-Surge line"; -string const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_TITLE = "SWL-Setup-Surge-Runup line"; -string const VECTOR_PLOT_INVALID_NORMALS_TITLE = "INVALID coastline-normal profiles"; -string const VECTOR_PLOT_MEAN_WAVE_ENERGY_TITLE = "Mean wave energy"; -string const VECTOR_PLOT_NORMALS_TITLE = "Coastline-normal profiles"; -string const VECTOR_PLOT_POLYGON_BOUNDARY_TITLE = "Polygons"; -string const VECTOR_PLOT_POLYGON_NODES_TITLE = "Polygon nodes"; -string const VECTOR_PLOT_RUN_UP_TITLE = "Run up"; -string const VECTOR_PLOT_SHADOW_ZONE_BOUNDARY_TITLE = "Shadow zone boundary"; -string const VECTOR_PLOT_STORM_SURGE_TITLE = "Storm surge"; -string const VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT_TITLE = "Wave orientation and height"; -string const VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE_TITLE = "Wave energy since collapse"; -string const VECTOR_PLOT_WAVE_SETUP_TITLE = "Wave setup"; -string const VECTOR_PLOT_WAVE_TRANSECT_POINTS_TITLE = "Wave transect points (real and synthetic)"; - -// Time series codes -string const TIME_SERIES_BEACH_CHANGE_NET_CODE = "beach_change_net"; -string const TIME_SERIES_BEACH_CHANGE_NET_NAME = "beach_change_net"; -string const TIME_SERIES_BEACH_DEPOSITION_CODE = "beach_deposition"; -string const TIME_SERIES_BEACH_DEPOSITION_NAME = "beach_deposition"; -string const TIME_SERIES_BEACH_EROSION_CODE = "beach_erosion"; -string const TIME_SERIES_BEACH_EROSION_NAME = "beach_erosion"; -string const TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_CODE = "cliff_collapse_deposition"; -string const TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_NAME = "cliff_collapse_deposition"; -string const TIME_SERIES_CLIFF_COLLAPSE_EROSION_CODE = "cliff_collapse_erosion"; -string const TIME_SERIES_CLIFF_COLLAPSE_EROSION_NAME = "cliff_collapse_erosion"; -string const TIME_SERIES_CLIFF_COLLAPSE_NET_CODE = "cliff_collapse_net"; -string const TIME_SERIES_CLIFF_COLLAPSE_NET_NAME = "cliff_collapse_net"; -string const TIME_SERIES_CLIFF_NOTCH_ELEV_CODE = "cliff_notch"; -string const TIME_SERIES_CLIFF_NOTCH_ELEV_NAME = "cliff_notch"; -string const TIME_SERIES_FLOOD_SETUP_SURGE_CODE = "flood_setup_surge"; -string const TIME_SERIES_FLOOD_SETUP_SURGE_NAME = "flood_setup_surge"; -string const TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE = "flood_setup_surge_runup"; -string const TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_NAME = "flood_setup_surge_runup"; -string const TIME_SERIES_PLATFORM_EROSION_CODE = "platform_erosion"; -string const TIME_SERIES_PLATFORM_EROSION_NAME = "platform_erosion"; -string const TIME_SERIES_SEA_AREA_CODE = "sea_area"; -string const TIME_SERIES_SEA_AREA_NAME = "sea_area"; -string const TIME_SERIES_SUSPENDED_SEDIMENT_CODE = "suspended"; -string const TIME_SERIES_SUSPENDED_SEDIMENT_NAME = "suspended_sediment"; -string const TIME_SERIES_SWL_CODE = "SWL"; -string const TIME_SERIES_SWL_NAME = "SWL"; - -// CShore stuff -string const WAVE_ENERGY_FLUX = "wave_energy_flux"; -string const WAVE_HEIGHT_X_FILENAME = "wave_height_x.csv"; -string const WAVE_HEIGHT_Y_FILENAME = "wave_height_y.csv"; -string const ACTIVE_ZONE_FILENAME = "activezone.csv"; - -//================================================ Globally-available functions ================================================= -template -T tMax(T a, T b) -{ - return ((a > b) ? a : b); -} - -template -T tMax(T a, T b, T c) -{ - T max = (a < b) ? b : a; - return ((max < c) ? c : max); -} - -template -T tMin(T a, T b) -{ - return ((a < b) ? a : b); -} - -template -T tMin(T a, T b, T c) -{ - return (a < b ? (a < c ? a : c) : (b < c ? b : c)); -} - -template -T tAbs(T a) -{ - // From a posting dated 18 Nov 93 by rmartin@rcmcon.com (Robert Martin), archived in cpp_tips - return ((a < 0) ? -a : a); -} - -// template -// bool bIsBetween(T a, T b, T c) -// { -// // Assumes b > c -// return ((a >= b) && (a <= c)); -// } - -template -string strDblToStr(const T &t) -{ - // From http://stackoverflow.com/questions/2125880/convert-float-to-stdstring-in-c - ostringstream os; - os << t; - return os.str(); -} - -// ============================================================================================================================== -// For comparison of two floating-point numbers, with a specified accuracy -// ============================================================================================================================== -template -bool bFPIsEqual(const T d1, const T d2, const T dEpsilon) -{ - // Since the accuracy of floating-point numbers varies with their magnitude, we must compare them by using an accuracy threshold which is relative to the magnitude of the two numbers being compared. This is a blend of an example from Knuth's 'The Art of Computer Programming. Volume 1. Fundamental Algorithms' and a posting dated 18 Nov 93 by rmartin@rcmcon.com (Robert Martin), archived in cpp_tips - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" - - if ((0 == d1) && (tAbs(d2) < dEpsilon)) - return true; - else if ((0 == d2) && (tAbs(d1) < dEpsilon)) - return true; - else - return ((tAbs(d1 - d2) < (dEpsilon * tAbs(d1))) ? true : false); - -#pragma GCC diagnostic pop -} - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -// Definitions are in utilsglobal.cpp -double dRound(double const); -int nRound(double const); -// bool bIsWhole(double const); -bool bIsStringValidDouble(string &); -bool bIsStringValidInt(string &); - -struct FillToWidth -{ - FillToWidth(char f, int w) : chFill(f), nWidth(w) {} - char chFill; - int nWidth; -}; - -//! Struct to hold wave data for a single transect/profile -struct TransectWaveData -{ - std::vector VdX; - std::vector VdY; - std::vector VdHeightX; - std::vector VdHeightY; - std::vector VbBreaking; - int nCoastID; - int nProfileID; - bool bIsGridEdge; -}; - -ostream &operator<<(ostream &, const FillToWidth &); - -// string strDbl(double const, int const); -string strDblRight(double const, int const, int const, bool const = true); -string strIntRight(int const, int const); -string strCentre(const char *, int const); -string strCentre(const string &, int const); -string strRight(const string &, int const); -string strRight(const char *, int const); -string strLeft(const string &, int const); -string strLeft(const char *, int const); -string strRightPerCent(double const, double const, int const, int const, - bool const = true); -#endif - -//================================================= debugging stuff ============================================================= -// #define CLOCKCHECK // Uncomment to check CPU clock rollover settings - -#endif // CME_H +/*! + \mainpage + \section intro_sec Introduction + + CoastalME (Coastal Modelling Environment) simulates the long-term behaviour of a coast. This initial version considers only simple soft cliff cross-shore effects. However, development of CoastalME is ongoing. Watch this space!\n\n + + CoastalME was devised and constructed by Andres Payo Garcia (British Geological Survey: agarcia@bgs.ac.uk) and David Favis-Mortlock (British Geological Survey: dfm1@bgs.ac.uk). We are very grateful to the following for support, assistance, and inspiration: Tom Ashby, Manuel Cobos Budia, Wilf Chun, Mark Dickson, Jim W. Hall, Martin D. Hurst, Matthew Ives, Robert J. Nicholls, Ian Townend, and Mike + J.A. Walkden.\n\n + + See https://github.com/coastalme/coastalme for the stable release version, and the unstable development version, of the source code.\n + \n + From Shingle Street\n + To Orford Ness\n + The waves maraud,\n + The winds oppress,\n + The earth can’t help\n + But acquiesce\n + For this is east\n + And east means loss,\n + A lessening shore, receding ground,\n + Three feet gone last year, four feet this\n + Where land runs out and nothing’s sound.\n + Nothing lasts long on Shingle Street.\n + \n + By Blake Morrison (2018). See https://www.penguin.co.uk/books/419911/shingle-street-by-morrison-blake/9780701188771\n + + \section install_sec Installing CoastalME + + \subsection install_step1 Obtaining the source code + + CoastalME builds easily using Linux. If you wish to run CoastalME on Windows, then we currently recommend using the Windows Subsystem Linux (WSL) software to do this. + + Create a local copy of the github repository, for example by downloading a zipfile, then unpacking it or cloning. We suggest unpacking it to something like "/home/YOUR NAME/Projects/CoastalME/", this is then your CoastalME folder. + + git clone https://github.com/coastalme/coastalme + + \subsection install_step2 Building CoastalME + + In a terminal window (i.e. at a command-line prompt) move to the CoastalME folder. + + Then move to the the src folder + + cd CoastalME/src + + and run run_cmake.sh + + ./run_cmake.sh + + If you get a "Permission denied" message: -bash: ./run_cmake.sh: Permission denied you will have to grant executable permission using chmod a+x run_cmake.sh, chmod a+x cshore/make_cshore.sh and then + ./run_cmake.sh + + This will build CShore, look for GDAL, and write the CMake files. If you see error messages about missing software (for example, telling you that CMake cannot be found or is too old, or GDAL cannot be found or is too old) then you need to install or update the software that is causing the problem. + + Next, run + + make install + + This will create an executable file called cme in the CoastalME folder. + + \section run_sec Running CoastalME + + \subsection run_step1 Specifying the input file + + Edit cme.ini to tell CoastalME which input file to read (for example, in/test_suite/minimal_wave_angle_230/minimal.dat). + + \subsection run_step2 Running CoastalME + + Leave the src folder, and run cme + + cd .. + ./cme + + Output will appear in the "Path for output" folder. + + \subsection run_step3 Running CoastalME's test suite + + To check that your installation is running correctly, you can run a suite of pre-defined tests by running the following commands: + + chmod a+x run_test_suite.sh + ./run_test_suite.sh + + The `chmod` comand ensures that you have permission to execute the run_test_suite.sh file. + + \subsection run_step4 Managing CoastalME's output + + Once you have CoastalME (CME) up and running, you can reduce the quantity of output (it can be overwhelming!) in several ways. + + Change "Content of log file" in the CME input file for any of the test suite runs (the name of this input file is listed in cme.ini, both are simple text files). If you set "Content of log file" to zero, then CME won't output a log file; setting it to 4 (all output) is really only useful to developers. + + Change "GIS vector files to output" and "GIS vector files to output" in the CME input file. These are both set to "all" in the test suite files on GitHub. Instead of "all" you can list the space-separated codes for only the GIS output that you want to see. A list of CME GIS output codes is in codes.txt. + + Enjoy! + + \file cme.h + \brief This file contains global definitions for CoastalME +*/ + +/* + NOTE Before releasing a new version, do a pre-release build to check for memory leaks with -fsanitize options enabled (see CMakeLists.txt) then run ./cme 2> sanitize.txt NOT UNDER DEBUG (i.e. not using gdb) + + TODOLIST + *********************************************************************************************************** + DOCUMENTATION + TODO 001 Add more Doxygen information about all classes + TODO 007 We now have setup and surge info from CShore (thanks to Manuel). But what shall we do with this info? "The variable VdWaveSetupSurge() represents the sea level rise due to wave effects (setup) and storm surge. CSHORE calculates them together and they can’t be separated. That’s what the VdWaveSetupSurge variable is. That’s why you saw my initial efforts to try to separate both variables from CSHORE commented out, which is impossible. What is possible is to get the RunUp from CSHORE, but since it uses an empirical formula for that, I finally decided to calculate it separately. To your question about whether you should remove VdStormSurge, the answer is yes. I left it because I still intend at some point to extract the cross-shore transport from CSHORE and balance it in CME with the longshore and cross-shore transports without needing the Dean profile. From my point of view, this would be even more realistic, though at first it will surely drive us crazy." + + USER INPUT + TODO 000 Should user input be split in two main files: one for frequently-changed things, one for rarely-changed things? If so, what should go into each file ('testing only' OK, but what else?) + TODO 011 Should this constant be a user input? If so, TODO 071 + TODO 036 Read in changed deep water wave values (need TODO 071) + TODO 022 Get intervention update working (need TODO 071) + TODO 042 Should we have a smallest valid input for KLS in the CERC equation? + TODO 045 Method of getting depth of closure value needs to be a user input (need TODO 071) + TODO 049 Handle other command line parameters e.g. path to .ini file, path to datafile + TODO 035 Also handle other EPSG for vector spatial reference systems + TODO 054 Choose more files to omit from "usual" raster output + TODO 069 Enable ability to represent intervention structures which have their foundation embedded in consolidated sediment. In other words, with the elevation of the base of the intervention structure *below* the top of all consolidated sediment layers. Will need some sanity checking of elevations + TODO 071 If the user input file format is changed, write a Python script to convert from the old file format to the new + TODO 083 Get all three kinds of sediment input events working correctly + + ERROR HANDLING + TODO 038 Do better error handling if insufficient memory + TODO 004 Improve error handling of situation where we have a valid shadow zone but cannot find a neighbouring cell which is 'under' the coastline + TODO 006 Check GDALGridCreate() with only start-of-coast or an end-of-coast profiles + TODO 009 Decide what to do when we have eroded down to basement + TODO 017 Extra safety check needed, make sure that each point is within valid grid + TODO 018 Improve situation where new landwards point on parallel profile is not within the raster grid + TODO 019 Improve situation where Dean profile has a near-zero elevation difference + TODO 020 Check calculation of elevation of coast point of Dean parallel profile + TODO 021 Improve situation where all layers have zero thickness + TODO 025 Improve situation where this point has only zero thickness layers + TODO 026 Check situation where cell in parallel profile is not in a polygon + TODO 028 Give a warning if raster input layer has several bands + TODO 053 Improve handling of situation where landward elevation of profile is -ve + TODO 055 Maybe add a safety check here? + TODO 080 Do we get -ve breaking wave heights here? + TODO 084 Improve handling of situation where consecutive profile points are same distance from shoreline + + THEORY/EFFICIENCY + TODO 002 Do we really need D50 for drift landform class? What do we need for drift? + TODO 005 Maybe give every coast point a value for end-of-profile wave height and direction instead of for deep water wave height and direction + TODO 010 Do we also need to update the active zone cells? + TODO 012 Change finding of adjacent polygons, and calculation of the length of shared normals, when we make polygon seaward length determined by depth of closure + TODO 013 Change calculation (need user input?) of coastline smoothing convexity threshold + TODO 014 Profile spacing, could try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity + TODO 016 Check mass balance for recirculating unconsolidated sediment option + TODO 023 Only calculate shore platform erosion if cell is in a polygon + TODO 024 Should we calculate platform erosion on a profile that has hit dry land? + TODO 044 Implement estuaries + TODO 051 Implement other ways of calculating depth of closure, see TODO 045 + TODO 056 Check this please Andres + TODO 059 Implement dune landform class + TODO 060 Remove 'magic numbers' from code here + TODO 061 Is this safety check to depth of breaking a reasonable thing to do? + TODO 066 Should this be for all layers? Check + TODO 067 Suspended fine sediment never decreases i.e. no suspended fine sediment ever leaves the grid. Is this OK? + TODO 070 Change CShore to use allocatable arrays (https://fortran-lang.org/en/learn/best_practices/allocatable_arrays/) so that the number of points in the CShore output profiles can either be a user input, or determined by e.g. the physical length of the profile. At present, max is NN = 1000 in cshore_wrapper.f03 + TODO 075 What if bedrock sticks above Dean profile? + TODO 076 When doing parallel profiles, start from the profile which is closest to a right angle with the coast + TODO 077 As traverse between the bounding profiles creating parallel profiles, gradually change the parallel profile orientation based on distance weighting of two bounding profiles + TODO 078 At present, we don't allow cliff collapse onto interventions. Is this realistic? Should it be different for different types on intervention? + TODO 089 Why do we get patches of sediment in the sea? + TODO 086 Try these as a more efficient replacement for GDALGridCreate(): https://github.com/delfrrr/delaunator-cpp https://www.cs.cmu.edu/~quake/triangle.html https://github.com/greenm01/poly2tri https://gts.sourceforge.net/index.html + TODO 088 In (almost) all whole-grid loops, immediately continue if cell is hinterland (but not when calculating cliff collapse) + TODO 090 At present, sediment cannot move from a given coastline polygon to a polygon belonging to another coastline. Is this always true? + TODO 092 If we have only fine sediment, the surface formed as the coast recedes inland is dead level (because fine sediment goes to suspension, and hence the Dean profile stuff does not operate). This causes problems with profile creation and CShore. Need to impose a small slope here somehow + TODO 093 There are a number of cell attributes that are really only useful for debugging. To keep memory usage down on release versions, need to flag these attributes and their methods so that they are included only in debug versions + TODO 094 Problems with sediment recirculation when large volumes of sedimentare introduced at the input end of the coast: need to spread this input sediment over the whole of the grid-end polygon + TODO 095 Parallel profiles seem to create "streaks" of above-water sediment, these streaks interfere with subsequent profile creation (profiles which hit steaks arte marked as invalid). Investigate this + TODO 096 It is OK to specify OGRFieldDefn objects as const in recent versions (e.g. 14.2) of g++, but in older versions of g++ (e.g. 12.2) have problems with const here. Maybe make the const a pre-processor condion? + + OUTPUT + TODO 065 Get GPKG output working: GDAL 3.9.1 does not yet implement this correctly. Currently is OK for vector output (but is very slow), not yet working for raster output + TODO 063 Add NetCDF support, see https://trac.osgeo.org/gdal/wiki/NetCDF + TODO 064 Add support for grids that are not oriented N-S and W-E, but which are still rectangular. See https://gdal.org/en/stable/tutorials/geotransforms_tut.html + TODO 031 Get raster slice output working with multiple slices + TODO 032 Improve output scaling for DBL_NODATA situation + TODO 033 Also test and configure (e.g. by passing open() options) other vector output file formats + TODO 034 Also test and configure (e.g. by passing open() options) other raster output file formats + TODO 043 When outputting profiles, how do we deal with randomness of profile spacing (since profile location is determined by curvature)? + TODO 052 Improve saving of profiles and parallel profiles + TODO 062 Show end-of-iteration number of cells with sediment somewhere + TODO 068 Only show output in log file that is relevant to processes being simulated + TODO 074 Output history of what landforms are on a particular cell or cells. User inputs cell(s), how? + TODO 082 Also show m_dStartIterUnconsFineAllCells etc. in log file + + 096 is max + + COMPLETED + TODO 003 Make coastline curvature moving window size a user input DONE in 1.1.22 + TODO 046 Why is cliff collapse eroded during deposition (three size classes) no longer calculated? DONE IN 1.1.22 + TODO 058 Dave to check this DONE in 1.1.22 + TODO 039 Rewrite reading of multiple random number seeds DONE in 1.2.1, 8 Nov 2024 + TODO 041 Read in SWL per-timestep + BUG 002 Useless output e.g. clay layers even if no clay input DONE in 1.1.21 + BUG 003 Use mean SWL for elevations of Dean profiles DONE in 1.2.1, 27 Nov 2024 + BUG 004 Don't smooth intervention coastline DONE 1.2.1, 27 Nov 2024 + TODO 073 If output dir does not exist, then create it (ask user first) DONE 1.2.2, 28 Nov 2024 + TODO 047 Where is the GDAL description for the deep water wave stations vector file? DONE 1.2.3, 2 Dec 2024 + TODO 048 Where is the GDAL description for the flood input locations point or vector file? DONE 1.2.3, 2 Dec 2024 + TODO 027 Sort out GDAL problem with raster reference units DONE 1.2.3, 2 Dec 2024 + TODO 079 Do sanity checking on wave and tide input DONE 1.2.3, 2 Dec 2024 + TODO 072 CShore crashes occasionally, is it because of -ve Z values here? DONE 1.2.3, 2 Dec 2024 + TODO 050 Update for recent versions of Windows DONE 1.2.3, 2 Dec 2024 + TODO 037 Need more info on nFindIndex() DONE 1.2.3, 2 Dec 2024 Improve coast normals DONE 1.2.3, 20 Dec 2024 + TODO 057 Check this please Manuel DONE 1.2.4, 4 Jan 2025 + TODO 087 Is there a problem if profile is not long enough for user-input depth of closure? DONE 1.3.0 2 Feb 2025 +*/ + +#ifndef CME_H +#define CME_H +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include + +#include +using std::string; + +#include +using std::ostringstream; + +#include +using std::ostream; + +#include + +//===================================================== platform-specific stuff ================================================= +#ifdef _WIN32 + #define access _access + #define F_OK 0 // Test for file existence +#endif + +#ifdef _MSC_VER + // MS Visual C++ compiler, byte order is IEEE little-endian + #ifdef _DEBUG + #include // useful + #endif + + // clock_t is a signed long: see + long const CLOCK_T_MIN = LONG_MIN; + double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); + + #ifdef _M_ALPHA + string const PLATFORM = "MS Visual C++ for Alpha"; + #elif defined _M_IX86 + string const PLATFORM = "MS Visual C++ for Intel x86"; + #elif defined _M_MPPC + string const PLATFORM = "MS Visual C++ for Power PC"; + #elif defined _M_MRX000 + string const PLATFORM = "MS Visual C++ for MIPS"; + #else + string const PLATFORM = "MS Visual C++ for unknown CPU"; + #endif + +#elif defined __GNUG__ + // GNU compiler + #ifndef CPU + #error "CPU not defined" + #else + #ifdef x86 + // Intel x86, byte order is little-endian + string const PLATFORM = "GNU Compiler for Intel x86"; + // clock_t is an unsigned long: see + unsigned long const CLOCK_T_MIN = 0; + double const CLOCK_T_RANGE = static_cast(ULONG_MAX); + #elif defined rs6000 + // IBM RS-6000, byte order is big-endian + string const PLATFORM = "GNU complier for IBM RS-6000"; + // clock_t is a signed long: see NEED TO CHECK + long const CLOCK_T_MIN = LONG_MIN; + double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); + #elif defined ultrasparc + // Sun UltraSparc, byte order is big-endian + string const PLATFORM = "GNU compiler for Sun UltraSPARC"; + // clock_t is a signed long: see + long const CLOCK_T_MIN = LONG_MIN; + double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); + #else + // Something else + string const PLATFORM = "GNU compiler for unknown CPU"; + // clock_t is a signed long: NEED TO CHECK + long const CLOCK_T_MIN = LONG_MIN; + double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); + #endif + #endif + +#elif defined __clang__ + // Clang compiler + #ifndef CPU + #error "CPU not defined" + #else + #ifdef x86 + // Intel x86, byte order is little-endian + string const PLATFORM = "Clang compiler for Intel x86"; + // clock_t is an unsigned long: see + unsigned long const CLOCK_T_MIN = 0; + double const CLOCK_T_RANGE = static_cast(ULONG_MAX); + #else + // Something else + string const PLATFORM = "Clang compiler for unknown CPU"; + // clock_t is a signed long: NEED TO CHECK + long const CLOCK_T_MIN = LONG_MIN; + double const CLOCK_T_RANGE = static_cast(LONG_MAX) - static_cast(CLOCK_T_MIN); + #endif + #endif + +#elif defined __MINGW32__ + // Minimalist GNU for Windows + // #define __USE_MINGW_ANSI_STDIO 1 // Fix long doubles output problem, + // see http://stackoverflow.com/questions/7134547/gcc-printf-and-long-double-leads-to-wrong-output-c-type-conversion-messes-u + #define WEXITSTATUS(x) ((x) & 0xff) + +#elif defined __HP_aCC + // HP-UX aCC, byte order is big-endian, can be either 32-bit or 64-bit + string const PLATFORM = "HP-UX aC++"; + // clock_t is an unsigned long: see + unsigned long const CLOCK_T_MIN = 0; + #ifdef __ia64 + // However, clock_t is a 32-bit unsigned long and we are using 64-bit unsigned longs here + double const CLOCK_T_RANGE = 4294967295UL; // crude, improve + #else + double const CLOCK_T_RANGE = static_cast(ULONG_MAX); + #endif +#endif + +// TODO: Check +// #if defined(WIN32) +// #define STRCASECMP(a, b) (_stricmp(a, b)) +// #define STRNCASECMP(a, b, n) (_strnicmp(a, b, n)) +// #else +// /** Alias for strcasecmp() */ +// #define STRCASECMP(a, b) (strcasecmp(a, b)) +// /** Alias for strncasecmp() */ +// #define STRNCASECMP(a, b, n) (strncasecmp(a, b, n)) +// //# endif +// /** Alias for strncasecmp() == 0 */ +// #define EQUALN(a, b, n) (STRNCASECMP(a, b, n) == 0) +// /** Alias for strcasecmp() == 0 */ +// #define EQUAL(a, b) (STRCASECMP(a, b) == 0) +// #endif + +//===================================================== hard-wired constants ==================================================== +char const COLON = ':'; +char const COMMA = ','; +char const DASH = '-'; +char const PATH_SEPARATOR = '/'; // Works for Windows too! +char const QUOTE1 = ';'; +char const QUOTE2 = '#'; +char const SLASH = '/'; +char const SPACE = ' '; +char const TILDE = '~'; + +// TESTING options +bool const ACCEPT_TRUNCATED_PROFILES = true; +bool const CREATE_SHADOW_ZONE_IF_HITS_GRID_EDGE = true; // If shadow line tracing hits grid edge, create shadow zone? +bool const SAVE_CSHORE_OUTPUT = true; // #ifdef CSHORE_FILE_INOUT || CSHORE_BOTH, append all CShore output files to a whole-run master +bool const USE_DEEP_WATER_FOR_SHADOW_LINE = true; // Use deep water wave orientation in determining shadow line orientation? + +// Not likely that user will need to change these +int const NUMBER_OF_RNGS = 2; // Number of random number generators +int const SAVEMAX = 100000; // Maximum number of saves of spatial output +int const BUF_SIZE = 2048; // Max length (inc. terminating NULL) of any C-type string +int const CAPE_POINT_MIN_SPACING = 10; // In cells: for shadow zone stuff, cape points must not be closer than this +int const CLOCK_CHECK_ITERATION = 5000; // If have done this many timesteps then reset the CPU time running total +int const COAST_LENGTH_MAX = 100; // For safety check when tracing coast +int const COAST_LENGTH_MIN_X_PROF_SPACE = 20; // Ignore very short coasts less than this x profile spacing + +//! The size of the arrays output by CShore. If this is changed, then must also set the same value on line 12 of cshore_wrapper.f03 (integer, parameter :: NN = 1000, NL = 1) and recompile CShore. Eventually we should move to dynamically allocated arrays TODO 070 +int const CSHOREARRAYOUTSIZE = 1000; + +int const FLOOD_FILL_START_OFFSET = 2; // In cells: cell-by-cell fill starts this distance inside polygon +int const GRID_MARGIN = 10; // Ignore this many along-coast grid-edge points re. shadow zone calcs +int const INT_NODATA = -9999; // CME's internal NODATA value for ints +int const MAX_LEN_SHADOW_LINE_TO_IGNORE = 200; // In cells: if can't find cell-by-cell fill start point, continue if short shadow line +int const MAX_NUM_PREV_ORIENTATION_VALUES = 10; // Max length of deque used in tracing shadow boundary +int const MAX_NUM_SHADOW_ZONES = 10; // Consider at most this number of shadow zones +int const MIN_INLAND_OFFSET_UNCONS_EROSION = 5; // Used in estimation of beach erosion +int const MIN_PARALLEL_PROFILE_SIZE = 3; // In cells: min size for valid unconsolidated sediment parallel profile +int const MIN_PROFILE_SIZE = 3; // In cells: min size for valid unconsolidated sediment profile +int const DEFAULT_PROFILE_SPACING = 15; // In cells: profile creation does not work well if profiles are too closely spaced +int const SAVGOL_POLYNOMIAL_MAX_ORDER = 6; // Maximum order of Savitzky-Golay smoothing polynomial + +// Log file detail level +int const NO_LOG_FILE = 0; +int const LOG_FILE_LOW_DETAIL = 1; +int const LOG_FILE_MIDDLE_DETAIL = 2; +int const LOG_FILE_HIGH_DETAIL = 3; +int const LOG_FILE_ALL = 4; + +// Direction codes +int const NO_DIRECTION = 0; +int const NORTH = 1; +int const NORTH_EAST = 2; +int const EAST = 3; +int const SOUTH_EAST = 4; +int const SOUTH = 5; +int const SOUTH_WEST = 6; +int const WEST = 7; +int const NORTH_WEST = 8; + +int const DIRECTION_DOWNCOAST = 0; // Down-coast, i.e. along the coast so that the index of coastline points INCREASES +int const DIRECTION_UPCOAST = 1; // Up-coast, i.e. along the coast so that the index of coastline points DECREASES + +// Handedness codes, these show which side the sea is on when travelling down-coast (i.e. in the direction in which coastline point numbers INCREASE) +int const NULL_HANDED = -1; +int const RIGHT_HANDED = 0; +int const LEFT_HANDED = 1; + +// Sediment texture codes +int const TEXTURE_FINE = 0; +int const TEXTURE_SAND = 1; +int const TEXTURE_COARSE = 2; + +// Time unit codes +int const TIME_UNKNOWN = -1; +int const TIME_HOURS = 0; +int const TIME_DAYS = 1; +int const TIME_MONTHS = 2; +int const TIME_YEARS = 3; + +// Intervention input and output codes +int const IO_INTERVENTION_NONE = 0; +int const IO_INTERVENTION_STRUCT = 1; +int const IO_INTERVENTION_NON_STRUCT = 2; + +// Landform category codes +int const LF_UNKNOWN = 0; +int const LF_HINTERLAND = 1; +int const LF_SEA = 2; +int const LF_CLIFF_ON_COASTLINE = 6; +int const LF_CLIFF_INLAND = 7; +int const LF_DRIFT_TALUS = 9; +int const LF_DRIFT_BEACH = 10; +int const LF_DRIFT_DUNES = 11; // TODO 059 Implement dune landform class +int const LF_INTERVENTION_STRUCT = 12; +int const LF_INTERVENTION_NON_STRUCT = 13; +int const LF_ISLAND = 14; // Not yet implemented +int const LF_SEDIMENT_INPUT_UNCONSOLIDATED = 18; +int const LF_SEDIMENT_INPUT_CONSOLIDATED = 19; + +// GIS raster input codes +int const FINE_CONS_RASTER = 1; +int const SAND_CONS_RASTER = 2; +int const COARSE_CONS_RASTER = 3; +int const FINE_UNCONS_RASTER = 4; +int const SAND_UNCONS_RASTER = 5; +int const COARSE_UNCONS_RASTER = 6; +int const SUSP_SED_RASTER = 7; +int const LANDFORM_RASTER = 8; +int const INTERVENTION_CLASS_RASTER = 9; +int const INTERVENTION_HEIGHT_RASTER = 10; + +// GIS vector data type codes +int const VEC_FIELD_DATA_ANY = 0; +int const VEC_FIELD_DATA_INT = 1; +int const VEC_FIELD_DATA_REAL = 2; +int const VEC_FIELD_DATA_STRING = 3; +int const VEC_FIELD_DATA_OTHER = 4; + +// GIS vector geometry codes +int const VEC_GEOMETRY_POINT = 1; +int const VEC_GEOMETRY_LINE = 2; +int const VEC_GEOMETRY_POLYGON = 3; +int const VEC_GEOMETRY_OTHER = 4; + +// GIS vector input codes and constraints +int const DEEP_WATER_WAVE_STATIONS_VEC = 1; +int const DEEP_WATER_WAVE_STATIONS_MAX_LAYER = 1; +int const DEEP_WATER_WAVE_STATIONS_POINT_GEOMETRY = VEC_GEOMETRY_POINT; +int const SEDIMENT_INPUT_EVENT_LOCATION_VEC = 2; +int const SEDIMENT_INPUT_EVENT_LOCATION_MAX_LAYER = 1; +int const SEDIMENT_INPUT_EVENT_LOCATION_POINT_GEOMETRY = VEC_GEOMETRY_POINT; +int const FLOOD_LOCATION_POINT_GEOMETRY = VEC_GEOMETRY_POINT; +int const SEDIMENT_INPUT_EVENT_LOCATION_LINE_GEOMETRY = VEC_GEOMETRY_LINE; +int const FLOOD_LOCATION_VEC = 3; +int const FLOOD_LOCATION_MAX_LAYER = 1; + +// GIS raster output codes + +int const RASTER_PLOT_ACTIVE_ZONE = 1; +int const RASTER_PLOT_ACTUAL_BEACH_EROSION = 2; +int const RASTER_PLOT_ACTUAL_PLATFORM_EROSION = 3; +int const RASTER_PLOT_AVG_SEA_DEPTH = 4; +int const RASTER_PLOT_AVG_SUSPENDED_SEDIMENT = 5; +int const RASTER_PLOT_AVG_WAVE_HEIGHT = 6; +int const RASTER_PLOT_AVG_WAVE_ORIENTATION = 7; +int const RASTER_PLOT_BASEMENT_ELEVATION = 8; +int const RASTER_PLOT_BEACH_DEPOSITION = 9; +int const RASTER_PLOT_BEACH_MASK = 10; +int const RASTER_PLOT_BEACH_PROTECTION = 11; +int const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE = 12; +int const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND = 13; +int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE = 14; +int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE = 15; +int const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND = 16; +#ifdef _DEBUG +int const RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP = 17; +#endif +int const RASTER_PLOT_CLIFF_NOTCH_ALL = 18; +int const RASTER_PLOT_CLIFF_TOE = 19; +int const RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT = 20; +int const RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT = 21; +int const RASTER_PLOT_COAST = 22; +int const RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT = 23; +int const RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION = 24; +int const RASTER_PLOT_DEEP_WATER_WAVE_PERIOD = 25; +int const RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT = 26; +int const RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT = 27; +int const RASTER_PLOT_INTERVENTION_CLASS = 28; +int const RASTER_PLOT_INTERVENTION_HEIGHT = 29; +int const RASTER_PLOT_INUNDATION_MASK = 30; +int const RASTER_PLOT_LANDFORM = 31; +int const RASTER_PLOT_NORMAL_PROFILE = 32; +int const RASTER_PLOT_TOP_ELEV_INC_SEA = 33; +int const RASTER_PLOT_POLYGON = 34; +int const RASTER_PLOT_POLYGON_GAIN_OR_LOSS = 35; +int const RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT = 36; +int const RASTER_PLOT_POTENTIAL_BEACH_EROSION = 37; +int const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION = 38; +int const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK = 39; +int const RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT = 40; +int const RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT = 41; +int const RASTER_PLOT_SEA_DEPTH = 42; +int const RASTER_PLOT_SEDIMENT_INPUT = 43; +int const RASTER_PLOT_SED_TOP_INC_TALUS_ELEV = 44; +int const RASTER_PLOT_SETUP_SURGE_FLOOD_MASK = 45; +int const RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK = 46; +int const RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE = 47; +int const RASTER_PLOT_SHADOW_ZONE = 48; +int const RASTER_PLOT_SLICE = 49; +int const RASTER_PLOT_SLOPE_FOR_CLIFF_TOE = 50; +int const RASTER_PLOT_CONS_SED_SLOPE = 51; +int const RASTER_PLOT_SUSPENDED_SEDIMENT = 52; +int const RASTER_PLOT_TALUS = 53; +int const RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION = 54; +int const RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION = 55; +int const RASTER_PLOT_TOTAL_BEACH_DEPOSITION = 56; +int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE = 57; +int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND = 58; +int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE = 59; +int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE = 60; +int const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND = 61; +int const RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION = 62; +int const RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION = 63; +int const RASTER_PLOT_WAVE_FLOOD_LINE = 64; +int const RASTER_PLOT_WAVE_HEIGHT = 65; +int const RASTER_PLOT_WAVE_ORIENTATION = 66; + +// GIS vector output codes +// int const VECTOR_PLOT_FLOOD_SWL_SETUP_LINE = 19; +int const VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT = 1; +int const VECTOR_PLOT_BREAKING_WAVE_HEIGHT = 2; +int const VECTOR_PLOT_CLIFF_EDGE = 3; +int const VECTOR_PLOT_CLIFF_NOTCH_ACTIVE = 4; +int const VECTOR_PLOT_COAST = 5; +int const VECTOR_PLOT_COAST_CURVATURE = 6; +int const VECTOR_PLOT_COAST_SWL_HIGHEST = 7; +int const VECTOR_PLOT_COAST_SWL_LOWEST = 8; +int const VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT = 9; +int const VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY = 10; +int const VECTOR_PLOT_FLOOD_LINE = 11; +int const VECTOR_PLOT_INVALID_NORMALS = 12; +int const VECTOR_PLOT_MEAN_WAVE_ENERGY = 13; +int const VECTOR_PLOT_NORMALS = 14; +int const VECTOR_PLOT_POLYGON_BOUNDARY = 15; +int const VECTOR_PLOT_POLYGON_NODES = 16; +int const VECTOR_PLOT_RUN_UP = 17; +int const VECTOR_PLOT_SHADOW_ZONE_BOUNDARY = 18; +int const VECTOR_PLOT_STORM_SURGE = 19; +int const VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT = 20; +int const VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE = 21; +int const VECTOR_PLOT_WAVE_SETUP = 22; +int const VECTOR_PLOT_WAVE_TRANSECT_POINTS = 23; +// int const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE = 24; +// int const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE = 25; + +// Return codes +int const RTN_OK = 0; +int const RTN_HELP_ONLY = 1; +int const RTN_CHECK_ONLY = 2; +int const RTN_USER_ABORT = 3; +int const RTN_ERR_BADPARAM = 4; +int const RTN_ERR_INI = 5; +int const RTN_ERR_CMEDIR = 6; +int const RTN_ERR_RUNDATA = 7; +int const RTN_ERR_SCAPE_SHAPE_FUNCTION_FILE = 8; +int const RTN_ERR_TIDEDATAFILE = 9; +int const RTN_ERR_LOGFILE = 10; +int const RTN_ERR_OUTFILE = 11; +int const RTN_ERR_TSFILE = 12; +int const RTN_ERR_DEMFILE = 13; +int const RTN_ERR_RASTER_FILE_READ = 14; +int const RTN_ERR_VECTOR_FILE_READ = 15; +int const RTN_ERR_MEMALLOC = 16; +int const RTN_ERR_RASTER_GIS_OUT_FORMAT = 17; +int const RTN_ERR_VECTOR_GIS_OUT_FORMAT = 18; +int const RTN_ERR_TEXT_FILE_WRITE = 19; +int const RTN_ERR_RASTER_FILE_WRITE = 20; +int const RTN_ERR_VECTOR_FILE_WRITE = 21; +int const RTN_ERR_TIMESERIES_FILE_WRITE = 22; +int const RTN_ERR_LINETOGRID = 23; +int const RTN_ERR_PROFILE_SPACING = 24; +// 25 is now missing +int const RTN_ERR_PROFILE_ENDPOINT_IS_INLAND = 26; +int const RTN_ERR_NO_SOLUTION_FOR_ENDPOINT = 27; +int const RTN_ERR_PROFILE_END_INSUFFICIENT_DEPTH = 28; +int const RTN_ERR_NO_PROFILES_1 = 29; +int const RTN_ERR_NO_PROFILES_2 = 30; +int const RTN_ERR_NOSEACELLS = 31; +int const RTN_ERR_GRID_TO_LINE = 32; +int const RTN_ERR_NO_COAST = 34; +int const RTN_ERR_PROFILE_WRITE = 35; +int const RTN_ERR_TIME_UNITS = 36; +int const RTN_ERR_CLIFF_NOTCH = 37; +int const RTN_ERR_CLIFF_CANNOT_DEPOSIT_ALL = 38; +int const RTN_ERR_BAD_INDEX = 39; +int const RTN_ERR_EDGE_OF_GRID = 40; +int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_BEACH_EROSION = 42; +int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_UPCOAST_BEACH_DEPOSITION = 43; +int const RTN_ERR_NO_SEAWARD_END_OF_PROFILE_DOWNCOAST_BEACH_DEPOSITION = 44; +int const RTN_ERR_LANDFORM_TO_GRID = 45; +int const RTN_ERR_NO_TOP_LAYER = 46; +int const RTN_ERR_NO_ADJACENT_POLYGON = 47; +int const RTN_ERR_BAD_MULTILINE = 48; +int const RTN_ERR_CANNOT_INSERT_POINT = 49; +int const RTN_ERR_CANNOT_ASSIGN_COASTAL_LANDFORM = 50; +int const RTN_ERR_SHADOW_ZONE_FLOOD_FILL_NOGRID = 51; +int const RTN_ERR_SHADOW_ZONE_FLOOD_START_POINT = 52; +int const RTN_ERR_CSHORE_EMPTY_PROFILE = 53; +int const RTN_ERR_CSHORE_FILE_INPUT = 54; +int const RTN_ERR_READING_CSHORE_FILE_OUTPUT = 55; +int const RTN_ERR_WAVE_INTERPOLATION_LOOKUP = 56; +int const RTN_ERR_GRIDCREATE = 57; +int const RTN_ERR_COAST_CANT_FIND_EDGE_CELL = 58; +int const RTN_ERR_CSHORE_ERROR = 59; +int const RTN_ERR_NO_CELL_UNDER_COASTLINE = 60; +int const RTN_ERR_OPEN_DEEP_WATER_WAVE_DATA = 61; +int const RTN_ERR_READING_DEEP_WATER_WAVE_DATA = 62; +int const RTN_ERR_BOUNDING_BOX = 63; +int const RTN_ERR_READING_SEDIMENT_INPUT_EVENT = 64; +int const RTN_ERR_SEDIMENT_INPUT_EVENT = 65; +int const RTN_ERR_SEDIMENT_INPUT_EVENT_LOCATION = 66; +int const RTN_ERR_WAVESTATION_LOCATION = 67; +int const RTN_ERR_FLOOD_LOCATION = 68; +int const RTN_ERR_CLIFF_NOT_IN_POLYGON = 69; +int const RTN_ERR_CELL_MARKED_PROFILE_COAST_BUT_NOT_PROFILE = 70; +int const RTN_ERR_TRACING_FLOOD = 71; +int const RTN_ERR_NO_START_FINISH_POINTS_TRACING_COAST = 72; +int const RTN_ERR_NO_VALID_COAST = 73; +int const RTN_ERR_REPEATING_WHEN_TRACING_COAST = 74; +int const RTN_ERR_ZERO_LENGTH_COAST = 75; +int const RTN_ERR_COAST_TOO_SMALL = 77; +int const RTN_ERR_IGNORING_COAST = 78; +int const RTN_ERR_TOO_LONG_TRACING_COAST = 79; +int const RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE_DIFFERENT_COASTS = 80; +int const RTN_ERR_POINT_NOT_FOUND_IN_MULTILINE_DIFFERENT_COASTS = 81; +int const RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE = 82; +int const RTN_ERR_CELL_IN_POLY_BUT_NO_POLY_COAST = 83; +int const RTN_ERR_CLIFF_TALUS_TO_UNCONS = 84; +int const RTN_ERR_UNKNOWN = 999; + +// Elevation and 'slice' codes +int const ELEV_IN_BASEMENT = -1; +int const ELEV_ABOVE_SEDIMENT_TOP = -2; +int const NO_NONZERO_THICKNESS_LAYERS = -3; + +// Vector smoothing codes +int const SMOOTH_NONE = 0; +int const SMOOTH_RUNNING_MEAN = 1; +int const SMOOTH_SAVITZKY_GOLAY = 2; + +// Grid-edge boundary treatment for unconsolidated sediment movement +int const GRID_EDGE_CLOSED = 0; +int const GRID_EDGE_OPEN = 1; +int const GRID_EDGE_RECIRCULATE = 2; + +// Model for wave propagation +int const WAVE_MODEL_COVE = 0; +int const WAVE_MODEL_CSHORE = 1; + +// Equation for estimating erosion of unconsolidated sediment +int const UNCONS_SEDIMENT_EQUATION_CERC = 0; +int const UNCONS_SEDIMENT_EQUATION_KAMPHUIS = 1; + +int const CLIFF_COLLAPSE_LENGTH_INCREMENT = 10; // Increment the planview length of the cliff talus Dean profile, if we have not been able to deposit enough +int const PROFILE_CHECK_DIST_FROM_COAST = 20; // TEST TODO Used in checking shoreline-normal profiles for intersection +int const GAP_BETWEEN_DIFFERENT_COAST_PROFILES = 30; // In cells, is the gap between profile ends belonging to different coasts + +int const NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC = 30; // Number of days to average daily high water elevation + +int const NO_RUNUP_EQUATION = 0; // Runup not considered +int const RUNUP_EQUATION_NIELSEN_HANSLOW = 1; // Runup equation is Nielsen, P. & Hanslow, D. J. 1991. Wave Runup Distributions on Natural Beaches. Journal of Coastal Research, 7, 1139-1152. *** & DHI (2004) ??? +int const RUNUP_EQUATION_MASE = 2; // Runup equation is Mase, H. 1989. Random Wave Runup Height on Gentle Slope. Journal of Waterway, Port, Coastal, and Ocean Engineering, 115, 649-661. +int const RUNUP_EQUATION_STOCKDON = 3; // Runup equation is Stockdon, H. F., Holman, R. A., Howd, P. A. & Sallenger JR, A. H. 2006. Empirical parameterization of setup, swash, and runup. Coastal Engineering, 53, 573-588. + +unsigned long const MASK = 0xfffffffful; +unsigned long const SEDIMENT_INPUT_EVENT_ERROR = -1; +unsigned long const UNSIGNED_LONG_NODATA = 9999; + +double const PI = 3.141592653589793238462643; + +double const D50_FINE_DEFAULT = 0.0625; // In mm +double const D50_SAND_DEFAULT = 0.42; // In mm +double const D50_COARSE_DEFAULT = 19.0; // In mm + +double const BEACH_PROTECTION_HB_RATIO = 0.23; // The beach protection factor is this times breaking depth +double const WALKDEN_HALL_PARAM_1 = 3.25; // First parameter in Equation 4 from Walkden & Hall, 2005 +double const WALKDEN_HALL_PARAM_2 = 1.50; // Second parameter in Equation 4 from Walkden & Hall, 2005 + +double const DEPTH_OVER_DB_INCREMENT = 0.001; // Depth over DB increment for erosion potential look-up function +double const INVERSE_DEPTH_OVER_DB_INCREMENT = 1000; // Inverse of the above +double const DEAN_POWER = 2.0 / 3.0; // Dean profile exponent + +// TODO 011 Let the user define these CShore input parameters +double const CSHORE_FRICTION_FACTOR = 0.015; // Friction factor for CShore model +double const CSHORE_SURGE_LEVEL = 0.0; // TODO 007 + +double const TOLERANCE = 1e-7; // For bFPIsEqual, if too small (e.g. 1e-10), get spurious "rounding" errors +double const SED_ELEV_TOLERANCE = 1e-5; // Used to compare depth-equivalent sediment amounts in bFPIsEqual and elsewhere +double const MASS_BALANCE_TOLERANCE = 1e-5; // For bFPIsEqual, used to compare for mass balance checks +double const STRAIGHT_COAST_MAX_DETAILED_CURVATURE = -5; +double const STRAIGHT_COAST_MAX_SMOOTH_CURVATURE = -1; +double const MIN_LENGTH_OF_SHADOW_ZONE_LINE = 10; // Used in shadow line tracing +double const MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE = 5; // Used in shadow line tracing +double const CLIFF_COLLAPSE_HEIGHT_INCREMENT = 0.1; // Increment the fractional height of the cliff talus Dean profile, if we have not been able to deposit enough +double const INTERVENTION_PROFILE_SPACING_FACTOR = 0.5; // Profile spacing on interventions works better if it is smaller than profile spacing on coastline + +double const CLIFF_NOTCH_CUTOFF_DISTANCE = 2; // Cut-off SWL distance (m), measured downwards from the cliff notch apex: below this there is no notch incision +double const DBL_NODATA = -9999; + +string const PROGRAM_NAME = "Coastal Modelling Environment (CoastalME) version 1.4.0 (11 Nov 2025)"; +string const PROGRAM_NAME_SHORT = "CME"; +string const CME_INI = "cme.ini"; + +string const COPYRIGHT = "(C) 2025 Andres Payo and David Favis-Mortlock"; +string const LINE = "-------------------------------------------------------------------------------"; +string const DISCLAIMER1 = "This program is distributed in the hope that it will be useful, but WITHOUT ANY"; +string const DISCLAIMER2 = "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A"; +string const DISCLAIMER3 = "PARTICULAR PURPOSE. See the GNU General Public License for more details. You"; +string const DISCLAIMER4 = "should have received a copy of the GNU General Public License along with this"; +string const DISCLAIMER5 = "program; if not, contact the Free Software Foundation, Inc., 675 Mass Ave,"; +string const DISCLAIMER6 = "Cambridge, MA 02139, USA."; + +string const ABOUT = "simulates the long-term behaviour of a coast. This initial version considers only simple soft cliff cross-shore effects"; +string const THANKS = "Many thanks to:\n\tTom Ashby\n\tManuel Cobos Budia\n\tWilf Chun\n\tMark Dickson\n\tJim W. Hall\n\tMartin D. Hurst\n\tMatthew Ives\n\tRobert J. Nicholls\n\tIan Townend\n\tMike J.A. Walkden"; +string const GDAL_DRIVERS = "GDAL drivers"; + +string const USAGE = "Usage: cme [OPTION]..."; +string const USAGE1 = " --gdal List GDAL drivers"; +string const USAGE2 = " --about Information about this program"; +string const USAGE3 = " --help Display this text"; +string const USAGE4 = " --home=DIRECTORY Specify the location of the .ini file etc."; +string const USAGE5 = " --datafile=FILE Specify the location and name of the main datafile"; +string const USAGE6 = " --yaml Use YAML format for the main datafile"; + +string const START_NOTICE = "- Started on "; +string const INITIALIZING_NOTICE = "- Initializing"; +string const READING_FILE_LOCATIONS = " - Reading file locations: "; +string const READING_RUN_DATA = " - Reading run data file: "; +string const READING_BASEMENT = " - Reading basement DEM: "; +string const READING_RASTER_FILES = " - Reading raster GIS files"; +string const READING_LANDFORM_FILE = " - Landform class: "; +string const READING_INTERVENTION_CLASS_FILE = " - Intervention class: "; +string const READING_INTERVENTION_HEIGHT_FILE = " - Intervention height: "; +string const READING_SUSPENDED_SEDIMENT_FILE = " - Suspended sediment: "; +string const READING_UNCONS_FINE_SEDIMENT_FILE = " - Unconsolidated fine sediment (layer "; +string const READING_UNCONS_SAND_SEDIMENT_FILE = " - Unconsolidated sand sediment (layer "; +string const READING_UNCONS_COARSE_SEDIMENT_FILE = " - Unconsolidated coarse sediment (layer "; +string const READING_CONS_FINE_SEDIMENT_FILE = " - Consolidated fine sediment (layer "; +string const READING_CONS_SAND_SEDIMENT_FILE = " - Consolidated sand sediment (layer "; +string const READING_CONS_COARSE_SEDIMENT_FILE = " - Consolidated coarse sediment (layer "; +string const READING_VECTOR_FILES = " - Reading vector GIS files"; +string const READING_DEEP_WATER_WAVE_FILE = " - Deep water wave values: "; +string const READING_SED_INPUT_EVENT_FILE = " - Sediment input event values: "; +string const READING_FLOOD_LOCATION = " - Characteristic locations for flood: "; +string const READING_SCAPE_SHAPE_FUNCTION_FILE = " - Reading SCAPE shape function file"; +string const READING_TIDE_DATA_FILE = " - Reading tide data file: "; +string const ALLOCATE_MEMORY = " - Allocating memory for raster grid"; +string const ADD_LAYERS = " - Adding sediment layers to raster grid"; +string const INITIALIZING_FINAL = " - Finishing initialization"; +string const RUN_NOTICE = "- Running simulation"; +string const SIMULATING = "\r - Simulating "; +string const FINAL_OUTPUT = "- Writing final output"; +string const SEND_EMAIL = " - Sending email to "; +string const RUN_END_NOTICE = "Run ended at "; +string const PRESS_KEY = "Press any key to continue..."; + +string const ERROR_NOTICE = " with error code "; +string const EMAIL_ERROR = "Could not send email"; + +string const SCAPE_DIR = "scape/"; +string const SCAPE_SHAPE_FUNCTION_FILE = "ShapeFunction.dat"; +string const EROSION_POTENTIAL_LOOKUP_FILE = "ErosionPotential.csv"; + +string const CSHORE_DIR = "cshore/"; +string const CSHORE_INFILE = "infile"; + +string const ERR = "*** ERROR "; +string const WARN = "WARNING "; +string const NOTE = " Note "; + +string const MASS_BALANCE_ERROR = "MASS BALANCE ERROR"; + +string const PER_ITER_HEAD1 = "<-----ELAPSED----><--SEA-><----POTENTIAL---><-----------ACTUAL-----------><-----POTENTIAL-----><------------ACTUAL-------------><-----------ACTUAL------------><--SEDIMENT--><---CLIFF COLLAPSE--->"; + +string const PER_ITER_HEAD2 = " TIME DEPTH PLATFORM EROSION PLATFORM EROSION BEACH EROSION BEACH EROSION BEACH DEPOSITION INPUT EVENT EROSION DEPOSITION SED"; + +string const PER_ITER_HEAD3 = "Time Hours Years Avg % Sea All Erod % Sea All Erod <--sea avg-> % Sea All Erod % Sea All Erod <--sea avg-> % Sea All Depos <--sea--> <-coast avg-><--sea-->"; + +string const PER_ITER_HEAD4 = "Step Area Sea Area Area Sea Area F S C Area Sea Area Area Sea Area F S C Area Sea Area S C F S C F S C S C F"; + +string const PER_ITER_HEAD5 = " Avg Avg Avg Avg Avg Avg Avg Avg Avg"; + +string const PER_ITER_HEAD = "PER-ITERATION RESULTS ============================================================================================================================================================================================="; + +string const PER_ITER_CSV_HEAD = "Timestep,Hours, Years, AvgSeaDepth_m, PotPlatformErosion_PctSeaArea, PotPlatformErosion_AllAvg_mm, PotPlatformErosion_ErodAvg_mm, ActPlatformErosion_PctSeaArea, ActPlatformErosion_AllAvg_mm, ActPlatformErosion_ErodAvg_mm, ActPlatformErosion_Fine_mm, ActPlatformErosion_Sand_mm, ActPlatformErosion_Coarse_mm, PotBeachErosion_PctSeaArea, PotBeachErosion_AllAvg_mm, PotBeachErosion_ErodAvg_mm, ActBeachErosion_PctSeaArea, ActBeachErosion_AllAvg_mm, ActBeachErosion_ErodAvg_mm, ActBeachErosion_Fine_mm, ActBeachErosion_Sand_mm, ActBeachErosion_Coarse_mm, BeachDeposition_PctSeaArea, BeachDeposition_AllAvg_mm, BeachDeposition_DepAvg_mm, BeachDeposition_Sand_mm, BeachDeposition_Coarse_mm, SedimentInput_Fine, SedimentInput_Sand, SedimentInput_Coarse, CliffCollapse_Fine_mm, CliffCollapse_Sand_mm, CliffCollapse_Coarse_mm, CliffDeposition_Sand_mm, CliffDeposition_Coarse_mm, SuspendedSediment_mm, GISEvents"; + +string const ENDHYDROLOGYHEAD = "END OF SIMULATION: HYDROLOGY ======================================================================================================================================================================================"; +string const ENDSEDIMENTHEAD = "END OF SIMULATION: SEDIMENT MOVEMENT =============================================================================================================================================================================="; +string const PERFORMHEAD = "END OF SIMULATION: PERFORMANCE ===================================================================================================================================================================================="; + +string const OUTEXT = ".out"; +string const LOGEXT = ".log"; +string const CSVEXT = ".csv"; + +string const DEEP_WATER_WAVE_STATION_ID = "id"; +string const SEDIMENT_INPUT_EVENT_LOCATION_ID = "id"; +string const FLOOD_LOCATION_ID = "id"; + +// GIS raster output user codes +string const RASTER_ACTIVE_ZONE_CODE = "active_zone"; +string const RASTER_ACTIVE_ZONE_NAME = "active_zone"; +string const RASTER_ACTUAL_BEACH_EROSION_CODE = "actual_beach_erosion"; +string const RASTER_ACTUAL_BEACH_EROSION_NAME = "actual_beach_erosion"; +string const RASTER_ACTUAL_PLATFORM_EROSION_CODE = "actual_platform_erosion"; +string const RASTER_ACTUAL_PLATFORM_EROSION_NAME = "actual_platform_erosion"; +string const RASTER_ALL_OUTPUT_CODE = "all"; +string const RASTER_AVG_SEA_DEPTH_CODE = "avg_sea_depth"; +string const RASTER_AVG_SEA_DEPTH_NAME = "avg_sea_depth"; +string const RASTER_AVG_SUSP_SED_CODE = "avg_susp_sed"; +string const RASTER_AVG_SUSP_SED_NAME = "avg_susp_sed"; +string const RASTER_AVG_WAVE_HEIGHT_CODE = "avg_wave_height"; +string const RASTER_AVG_WAVE_HEIGHT_NAME = "avg_wave_height"; +string const RASTER_AVG_WAVE_ORIENTATION_CODE = "avg_wave_orientation"; +string const RASTER_AVG_WAVE_ORIENTATION_NAME = "avg_wave_orientation"; +string const RASTER_BASEMENT_ELEVATION_CODE = "basement_elevation"; +string const RASTER_BASEMENT_ELEVATION_NAME = "basement_elevation"; +string const RASTER_BEACH_DEPOSITION_CODE = "beach_deposition"; +string const RASTER_BEACH_DEPOSITION_NAME = "beach_deposition"; +string const RASTER_BEACH_MASK_CODE = "beach_mask"; +string const RASTER_BEACH_MASK_NAME = "beach_mask"; +string const RASTER_BEACH_PROTECTION_CODE = "beach_protection"; +string const RASTER_BEACH_PROTECTION_NAME = "beach_protection"; +string const RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_CODE = "cliff_collapse_talus_deposition_coarse"; +string const RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME = "cliff_collapse_talus_deposition_coarse"; +string const RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_CODE = "cliff_collapse_talus_deposition_sand"; +string const RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME = "cliff_collapse_talus_deposition_sand"; +string const RASTER_CLIFF_COLLAPSE_EROSION_COARSE_CODE = "cliff_collapse_erosion_coarse"; +string const RASTER_CLIFF_COLLAPSE_EROSION_COARSE_NAME = "cliff_collapse_erosion_coarse"; +string const RASTER_CLIFF_COLLAPSE_EROSION_FINE_CODE = "cliff_collapse_erosion_fine"; +string const RASTER_CLIFF_COLLAPSE_EROSION_FINE_NAME = "cliff_collapse_erosion_fine"; +string const RASTER_CLIFF_COLLAPSE_EROSION_SAND_CODE = "cliff_collapse_erosion_sand"; +string const RASTER_CLIFF_COLLAPSE_EROSION_SAND_NAME = "cliff_collapse_erosion_sand"; +string const RASTER_CLIFF_COLLAPSE_TIMESTEP_CODE = "cliff_collapse_timestep"; +string const RASTER_CLIFF_COLLAPSE_TIMESTEP_NAME = "cliff_collapse_timestep_all"; +string const RASTER_CLIFF_NOTCH_ALL_CODE = "cliff_notch_all"; +string const RASTER_CLIFF_NOTCH_ALL_NAME = "cliff_notch_all"; +string const RASTER_CLIFF_TOE_NAME = "cliff_toe"; // Note no code for this, because is chosen by m_bCliffToeLocate in input file +string const RASTER_COARSE_CONS_CODE = "cons_sed_coarse"; +string const RASTER_COARSE_CONS_NAME = "cons_sed_coarse"; +string const RASTER_COARSE_UNCONS_CODE = "uncons_sed_coarse"; +string const RASTER_COARSE_UNCONS_NAME = "uncons_sed_coarse"; +string const RASTER_COAST_CODE = "rcoast"; +string const RASTER_COAST_NAME = "rcoast"; +string const RASTER_COAST_NORMAL_CODE = "rcoast_normal"; +string const RASTER_COAST_NORMAL_NAME = "rcoast_normal"; +string const RASTER_DEEP_WATER_WAVE_HEIGHT_CODE = "deep_water_wave_height"; +string const RASTER_DEEP_WATER_WAVE_HEIGHT_NAME = "deep_water_wave_height"; +string const RASTER_DEEP_WATER_WAVE_ORIENTATION_CODE = "deep_water_wave_orientation"; +string const RASTER_DEEP_WATER_WAVE_ORIENTATION_NAME = "deep_water_wave_orientation"; +string const RASTER_DEEP_WATER_WAVE_PERIOD_CODE = "deep_water_wave_period"; +string const RASTER_DEEP_WATER_WAVE_PERIOD_NAME = "deep_water_wave_period"; +string const RASTER_FINE_CONS_CODE = "cons_sed_fine"; +string const RASTER_FINE_CONS_NAME = "cons_sed_fine"; +string const RASTER_FINE_UNCONS_CODE = "uncons_sed_fine"; +string const RASTER_FINE_UNCONS_NAME = "uncons_sed_fine"; +string const RASTER_INTERVENTION_CLASS_CODE = "intervention_class"; +string const RASTER_INTERVENTION_CLASS_NAME = "intervention_class"; +string const RASTER_INTERVENTION_HEIGHT_CODE = "intervention_height"; +string const RASTER_INTERVENTION_HEIGHT_NAME = "intervention_height"; +string const RASTER_INUNDATION_MASK_CODE = "inundation_mask"; +string const RASTER_INUNDATION_MASK_NAME = "inundation_mask"; +string const RASTER_LANDFORM_CODE = "landform_class"; +string const RASTER_LANDFORM_NAME = "landform_class"; +string const RASTER_POLYGON_CODE = "polygon_raster"; +string const RASTER_POLYGON_GAIN_OR_LOSS_CODE = "polygon_gain_or_loss"; +string const RASTER_POLYGON_GAIN_OR_LOSS_NAME = "polygon_gain_or_loss"; +string const RASTER_POLYGON_NAME = "polygon_raster"; +string const RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_CODE = "polygon_updrift_or_downdrift"; +string const RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_NAME = "polygon_updrift_or_downdrift"; +string const RASTER_POTENTIAL_BEACH_EROSION_CODE = "potential_beach_erosion"; +string const RASTER_POTENTIAL_BEACH_EROSION_NAME = "potential_beach_erosion"; +string const RASTER_POTENTIAL_PLATFORM_EROSION_CODE = "potential_platform_erosion"; +string const RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE = "potential_platform_erosion_mask"; +string const RASTER_POTENTIAL_PLATFORM_EROSION_MASK_NAME = "potential_platform_erosion_mask"; +string const RASTER_POTENTIAL_PLATFORM_EROSION_NAME = "potential_platform_erosion"; +string const RASTER_SAND_CONS_CODE = "cons_sed_sand"; +string const RASTER_SAND_CONS_NAME = "cons_sed_sand"; +string const RASTER_SAND_UNCONS_CODE = "uncons_sed_sand"; +string const RASTER_SAND_UNCONS_NAME = "uncons_sed_sand"; +string const RASTER_SEA_DEPTH_CODE = "sea_depth"; +string const RASTER_SEA_DEPTH_NAME = "sea_depth"; +string const RASTER_SEDIMENT_INPUT_EVENT_CODE = "sediment_input_total"; +string const RASTER_SEDIMENT_INPUT_EVENT_NAME = "sediment_input_total"; +string const RASTER_SEDIMENT_TOP_CODE = "sediment_top_elevation"; +string const RASTER_SEDIMENT_TOP_ELEVATION_NAME = "sediment_top_elevation"; +string const RASTER_SETUP_SURGE_FLOOD_MASK_CODE = "flood_setup_surge_mask"; +string const RASTER_SETUP_SURGE_FLOOD_MASK_NAME = "flood_setup_surge_mask"; +string const RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_CODE = "flood_setup_surge_runup_mask"; +string const RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_NAME = "flood_setup_surge_runup_mask"; +string const RASTER_SHADOW_DOWNDRIFT_ZONE_CODE = "shadow_downdrift_zones"; +string const RASTER_SHADOW_DOWNDRIFT_ZONE_NAME = "shadow_downdrift_zones"; +string const RASTER_SHADOW_ZONE_CODE = "shadow_zones"; +string const RASTER_SHADOW_ZONE_NAME = "shadow_zones"; +string const RASTER_SLICE_CODE = "slice"; +string const RASTER_SLICE_NAME = "slice"; +string const RASTER_SLOPE_FOR_CLIFF_TOE_NAME = "toe_slope"; // Note no code for this, because is chosen by m_bCliffToeLocate in input file +string const RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_CODE = "cons_sediment_slope"; +string const RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_NAME = "cons_sediment_slope"; +string const RASTER_SUSP_SED_CODE = "susp_sed"; +string const RASTER_SUSP_SED_NAME = "susp_sed"; +string const RASTER_TALUS_CODE = "talus"; +string const RASTER_TALUS_NAME = "talus"; +string const RASTER_TOP_ELEVATION_INC_SEA_CODE = "top_elevation"; +string const RASTER_TOP_ELEVATION_INC_SEA_NAME = "top_elevation_inc_sea"; +string const RASTER_TOTAL_ACTUAL_BEACH_EROSION_CODE = "total_actual_beach_erosion"; +string const RASTER_TOTAL_ACTUAL_BEACH_EROSION_NAME = "total_actual_beach_erosion"; +string const RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_CODE = "total_actual_platform_erosion"; +string const RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_NAME = "total_actual_platform_erosion"; +string const RASTER_TOTAL_BEACH_DEPOSITION_CODE = "total_beach_deposition"; +string const RASTER_TOTAL_BEACH_DEPOSITION_NAME = "total_beach_deposition"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_CODE = "total_cliff_collapse_talus_deposition_coarse"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME = "total_cliff_collapse_talus_deposition_coarse"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_CODE = "total_cliff_collapse_talus_deposition_sand"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME = "total_cliff_collapse_talus_deposition_sand"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_CODE = "total_cliff_collapse_erosion_coarse"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_NAME = "total_cliff_collapse_erosion_coarse"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_CODE = "total_cliff_collapse_erosion_fine"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_NAME = "total_cliff_collapse_erosion_fine"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_CODE = "total_cliff_collapse_erosion_sand"; +string const RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_NAME = "total_cliff_collapse_erosion_sand"; +string const RASTER_TOTAL_POTENTIAL_BEACH_EROSION_CODE = "total_potential_beach_erosion"; +string const RASTER_TOTAL_POTENTIAL_BEACH_EROSION_NAME = "total_potential_beach_erosion"; +string const RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_CODE = "total_potential_platform_erosion"; +string const RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_NAME = "total_potential_platform_erosion"; +string const RASTER_USUAL_OUTPUT_CODE = "usual"; +string const RASTER_WAVE_FLOOD_LINE_CODE = "wave_flood_line"; +string const RASTER_WAVE_FLOOD_LINE_NAME = "wave_flood_line"; +string const RASTER_WAVE_HEIGHT_CODE = "wave_height"; +string const RASTER_WAVE_HEIGHT_NAME = "wave_height"; +string const RASTER_WAVE_ORIENTATION_CODE = "wave_orientation"; +string const RASTER_WAVE_ORIENTATION_NAME = "wave_orientation"; +string const RASTER_WAVE_PERIOD_CODE = "wave_period"; +string const RASTER_WAVE_PERIOD_NAME = "wave_period"; + +// GIS raster output titles +string const RASTER_PLOT_ACTIVE_ZONE_TITLE = "Active zone"; +string const RASTER_PLOT_ACTUAL_BEACH_EROSION_TITLE = "Actual (constrained) beach erosion depth"; +string const RASTER_PLOT_ACTUAL_PLATFORM_EROSION_TITLE = "Actual (constrained) shore platform erosion depth"; +string const RASTER_PLOT_AVG_SEA_DEPTH_TITLE = "Average sea depth"; +string const RASTER_PLOT_AVG_SUSPENDED_SEDIMENT_TITLE = "Average depth of suspended sediment"; +string const RASTER_PLOT_AVG_WAVE_HEIGHT_TITLE = "Average wave height"; +string const RASTER_PLOT_AVG_WAVE_ORIENTATION_TITLE = "Average wave orientation"; +string const RASTER_PLOT_BASEMENT_ELEVATION_TITLE = "Basement elevation"; +string const RASTER_PLOT_BEACH_DEPOSITION_TITLE = "Beach deposition depth"; +string const RASTER_PLOT_BEACH_MASK_TITLE = "Beach mask"; +string const RASTER_PLOT_BEACH_PROTECTION_TITLE = "Beach protection factor"; +string const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE = "Depth of coarse talus from cliff collapse"; +string const RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE = "Depth of sand talus from cliff collapse"; +string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE_TITLE = "Cliff collapse depth of erosion, coarse sediment"; +string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE_TITLE = "Cliff collapse depth of erosion, fine sediment"; +string const RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND_TITLE = "Cliff collapse depth of erosion, sand sediment"; +#ifdef _DEBUG +string const RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP_TITLE = "Timestep at which cliff collapse occurred"; +#endif +string const RASTER_PLOT_CLIFF_NOTCH_ALL_TITLE = "All cliff notch incision"; +string const RASTER_PLOT_CLIFF_TOE_TITLE = "Cliff toe cells"; +string const RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated coarse sediment depth"; +string const RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated coarse sediment depth"; +string const RASTER_PLOT_COAST_TITLE = "Rasterized coastline"; +string const RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT_TITLE = "Deep water wave height"; +string const RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION_TITLE = "Deep water wave orientation"; +string const RASTER_PLOT_DEEP_WATER_WAVE_PERIOD_TITLE = "Deep water wave period"; +string const RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated fine sediment depth"; +string const RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated fine sediment depth"; +string const RASTER_PLOT_INTERVENTION_CLASS_TITLE = "Intervention class"; +string const RASTER_PLOT_INTERVENTION_HEIGHT_TITLE = "Intervention height"; +string const RASTER_PLOT_INUNDATION_MASK_TITLE = "Inundated area mask"; +string const RASTER_PLOT_LANDFORM_TITLE = "Landform class"; +string const RASTER_PLOT_NORMAL_PROFILE_TITLE = "Rasterized normal profiles"; +string const RASTER_PLOT_POLYGON_GAIN_OR_LOSS_TITLE = "Polygon gain or loss of unconsolidated sediment"; +string const RASTER_PLOT_POLYGON_TITLE = "Rasterized polygon boundaries"; +string const RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT_TITLE = "Polygon updrift or downdrift movement of unconsolidated sediment"; +string const RASTER_PLOT_POTENTIAL_BEACH_EROSION_TITLE = "Potential (unconstrained) beach erosion depth"; +string const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK_TITLE = "Potential (unconstrained) shore platform erosion binary mask"; +string const RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_TITLE = "Potential (unconstrained) shore platform erosion depth"; +string const RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT_TITLE = "Consolidated sand sediment depth"; +string const RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT_TITLE = "Unconsolidated sand sediment depth"; +string const RASTER_PLOT_SEA_DEPTH_TITLE = "Sea depth"; +string const RASTER_PLOT_SEDIMENT_INPUT_EVENT_TITLE = "Sediment input event(s) since last GIS save"; +string const RASTER_PLOT_SED_TOP_INC_TALUS_ELEV_TITLE = "Elevation of sediment top inc talus"; +string const RASTER_PLOT_SETUP_SURGE_FLOOD_MASK_TITLE = "Mask of setup-surge flood"; +string const RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK_TITLE = "Mask of setup-surge-runup flood"; +string const RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE_TITLE = "Downdrift of wave shadow zones"; +string const RASTER_PLOT_SHADOW_ZONE_TITLE = "Wave shadow zones"; +string const RASTER_PLOT_SLICE_TITLE = "Slice though layers at elevation = "; +string const RASTER_PLOT_SLOPE_FOR_CLIFF_TOE_TITLE = "Slope"; +string const RASTER_PLOT_CONS_SED_SLOPE_TITLE = "Local slope of consolidated sediment"; +string const RASTER_PLOT_SUSPENDED_SEDIMENT_TITLE = "Suspended sediment depth"; +string const RASTER_PLOT_TALUS_TITLE = "Talus from cliff collapse"; +string const RASTER_PLOT_TOP_ELEV_INC_SEA_TITLE = "Topmost elevation (sediment plus intervention plus se"; +string const RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION_TITLE = "Total actual (constrained) beach erosion depth"; +string const RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION_TITLE = "Total actual (constrained) shore platform erosion depth"; +string const RASTER_PLOT_TOTAL_BEACH_DEPOSITION_TITLE = "Total beach deposition depth"; +string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE = "Total depth of coarse talus from cliff collapse"; +string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE = "Total depth of sand talus from cliff collapse"; +string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_TITLE = "Total of cliff collapse erosion depth, coarse"; +string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_TITLE = "Total of cliff collapse erosion depth, fine"; +string const RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_TITLE = "Total of cliff collapse erosion depth, sand"; +string const RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION_TITLE = "Total potential (unconstrained) beach erosion depth"; +string const RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION_TITLE = "Total potential (unconstrained) shore platform erosion depth"; +string const RASTER_PLOT_WAVE_FLOOD_LINE_TITLE = "Wave flood line"; +string const RASTER_PLOT_WAVE_HEIGHT_TITLE = "Wave height"; +string const RASTER_PLOT_WAVE_ORIENTATION_TITLE = "Wave orientation"; + +// GIS vector output user codes +string const VECTOR_ALL_OUTPUT_CODE = "all"; +string const VECTOR_ALL_RIVER_FLOOD_OUTPUT_CODE = "all"; +string const VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_CODE = "avg_wave_angle"; +string const VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_NAME = "avg_wave_angle"; +string const VECTOR_BREAKING_WAVE_HEIGHT_CODE = "breaking_wave_height"; +string const VECTOR_BREAKING_WAVE_HEIGHT_NAME = "breaking_wave_height"; +string const VECTOR_CLIFF_EDGE_CODE = "cliff_edge"; +string const VECTOR_CLIFF_EDGE_NAME = "cliff_edge"; +string const VECTOR_CLIFF_NOTCH_ACTIVE_CODE = "cliff_notch_active"; +string const VECTOR_CLIFF_NOTCH_ACTIVE_NAME = "cliff_notch_active"; +string const VECTOR_COAST_CODE = "coast"; +string const VECTOR_COAST_CURVATURE_CODE = "coast_curvature"; +string const VECTOR_COAST_CURVATURE_NAME = "coast_curvature"; +string const VECTOR_COAST_NAME = "coast"; +string const VECTOR_COAST_SWL_HIGHEST_NAME = "coast_SWL_highest"; +string const VECTOR_COAST_SWL_LOWEST_NAME = "coast_SWL_lowest"; +string const VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_CODE = "deep_water_wave_angle"; +string const VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_NAME = "deep_water_wave_angle"; +string const VECTOR_DOWNDRIFT_ZONE_BOUNDARY_CODE = "downdrift_boundary"; +string const VECTOR_DOWNDRIFT_ZONE_BOUNDARY_NAME = "downdrift_boundary"; +string const VECTOR_FLOOD_LINE_CODE = "flood_line"; +string const VECTOR_FLOOD_LINE_NAME = "flood_line"; +string const VECTOR_FLOOD_SWL_SETUP_LINE_CODE = "setup"; +string const VECTOR_FLOOD_SWL_SETUP_LINE_NAME = "setup"; +string const VECTOR_FLOOD_SWL_SETUP_SURGE_LINE_CODE = "setup_surge"; +string const VECTOR_FLOOD_SWL_SETUP_SURGE_LINE_NAME = "setup_surge"; +string const VECTOR_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_CODE = "setup_surge_runup"; +string const VECTOR_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_NAME = "setup_surge_runup"; +string const VECTOR_INVALID_NORMALS_CODE = "invalid_normals"; +string const VECTOR_INVALID_NORMALS_NAME = "invalid_normals"; +string const VECTOR_MEAN_WAVE_ENERGY_CODE = "mean_wave_energy"; +string const VECTOR_MEAN_WAVE_ENERGY_NAME = "mean_wave_energy"; +string const VECTOR_NORMALS_CODE = "normals"; +string const VECTOR_NORMALS_NAME = "normals"; +string const VECTOR_POLYGON_BOUNDARY_CODE = "polygon"; +string const VECTOR_POLYGON_BOUNDARY_NAME = "polygon"; +string const VECTOR_POLYGON_NODE_CODE = "polygon_node"; +string const VECTOR_POLYGON_NODE_NAME = "polygon_node"; +string const VECTOR_RUN_UP_CODE = "run_up"; +string const VECTOR_RUN_UP_NAME = "run_up"; +string const VECTOR_SHADOW_ZONE_BOUNDARY_CODE = "shadow_boundary"; +string const VECTOR_SHADOW_ZONE_BOUNDARY_NAME = "shadow_boundary"; +string const VECTOR_STORM_SURGE_CODE = "storm_surge"; +string const VECTOR_STORM_SURGE_NAME = "storm_surge"; +string const VECTOR_USUAL_OUTPUT_CODE = "usual"; +string const VECTOR_WAVE_ANGLE_AND_HEIGHT_CODE = "wave_angle"; +string const VECTOR_WAVE_ANGLE_AND_HEIGHT_NAME = "wave_angle"; +string const VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_CODE = "wave_energy"; +string const VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_NAME = "wave_energy"; +string const VECTOR_WAVE_SETUP_CODE = "wave_setup"; +string const VECTOR_WAVE_SETUP_NAME = "wave_setup"; +string const VECTOR_WAVE_TRANSECT_POINTS_CODE = "wave_transect_points"; +string const VECTOR_WAVE_TRANSECT_POINTS_NAME = "wave_transect_points"; + +// GIS vector output titles +string const VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT_TITLE = "Average wave orientation and height"; +string const VECTOR_PLOT_BREAKING_WAVE_HEIGHT_TITLE = "Breaking wave height"; +string const VECTOR_PLOT_CLIFF_EDGE_TITLE = "Cliff edge"; +string const VECTOR_PLOT_CLIFF_NOTCH_ACTIVE_TITLE = "Active cliff notch incision"; +string const VECTOR_PLOT_COAST_CURVATURE_TITLE = "Coastline curvature"; +string const VECTOR_PLOT_COAST_SWL_HIGHEST_TITLE = "Coastline at highest SWL"; +string const VECTOR_PLOT_COAST_SWL_LOWEST_TITLE = "Coastline at lowest SWL"; +string const VECTOR_PLOT_COAST_TITLE = "Coastline"; +string const VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_TITLE = "Deep water wave orientation and height"; +string const VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY_TITLE = "Downdrift zone boundary"; +string const VECTOR_PLOT_FLOOD_LINE_TITLE = "Flood "; +string const VECTOR_PLOT_FLOOD_SWL_SETUP_LINE_TITLE = "SWL-Setup line"; +string const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE_TITLE = "SWL-Setup-Surge line"; +string const VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_TITLE = "SWL-Setup-Surge-Runup line"; +string const VECTOR_PLOT_INVALID_NORMALS_TITLE = "INVALID coastline-normal profiles"; +string const VECTOR_PLOT_MEAN_WAVE_ENERGY_TITLE = "Mean wave energy"; +string const VECTOR_PLOT_NORMALS_TITLE = "Coastline-normal profiles"; +string const VECTOR_PLOT_POLYGON_BOUNDARY_TITLE = "Polygons"; +string const VECTOR_PLOT_POLYGON_NODES_TITLE = "Polygon nodes"; +string const VECTOR_PLOT_RUN_UP_TITLE = "Run up"; +string const VECTOR_PLOT_SHADOW_ZONE_BOUNDARY_TITLE = "Shadow zone boundary"; +string const VECTOR_PLOT_STORM_SURGE_TITLE = "Storm surge"; +string const VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT_TITLE = "Wave orientation and height"; +string const VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE_TITLE = "Wave energy since collapse"; +string const VECTOR_PLOT_WAVE_SETUP_TITLE = "Wave setup"; +string const VECTOR_PLOT_WAVE_TRANSECT_POINTS_TITLE = "Wave transect points (real and synthetic)"; + +// Time series codes +string const TIME_SERIES_BEACH_CHANGE_NET_CODE = "beach_change_net"; +string const TIME_SERIES_BEACH_CHANGE_NET_NAME = "beach_change_net"; +string const TIME_SERIES_BEACH_DEPOSITION_CODE = "beach_deposition"; +string const TIME_SERIES_BEACH_DEPOSITION_NAME = "beach_deposition"; +string const TIME_SERIES_BEACH_EROSION_CODE = "beach_erosion"; +string const TIME_SERIES_BEACH_EROSION_NAME = "beach_erosion"; +string const TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_CODE = "cliff_collapse_deposition"; +string const TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_NAME = "cliff_collapse_deposition"; +string const TIME_SERIES_CLIFF_COLLAPSE_EROSION_CODE = "cliff_collapse_erosion"; +string const TIME_SERIES_CLIFF_COLLAPSE_EROSION_NAME = "cliff_collapse_erosion"; +string const TIME_SERIES_CLIFF_COLLAPSE_NET_CODE = "cliff_collapse_net"; +string const TIME_SERIES_CLIFF_COLLAPSE_NET_NAME = "cliff_collapse_net"; +string const TIME_SERIES_CLIFF_NOTCH_ELEV_CODE = "cliff_notch"; +string const TIME_SERIES_CLIFF_NOTCH_ELEV_NAME = "cliff_notch"; +string const TIME_SERIES_FLOOD_SETUP_SURGE_CODE = "flood_setup_surge"; +string const TIME_SERIES_FLOOD_SETUP_SURGE_NAME = "flood_setup_surge"; +string const TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE = "flood_setup_surge_runup"; +string const TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_NAME = "flood_setup_surge_runup"; +string const TIME_SERIES_PLATFORM_EROSION_CODE = "platform_erosion"; +string const TIME_SERIES_PLATFORM_EROSION_NAME = "platform_erosion"; +string const TIME_SERIES_SEA_AREA_CODE = "sea_area"; +string const TIME_SERIES_SEA_AREA_NAME = "sea_area"; +string const TIME_SERIES_SUSPENDED_SEDIMENT_CODE = "suspended"; +string const TIME_SERIES_SUSPENDED_SEDIMENT_NAME = "suspended_sediment"; +string const TIME_SERIES_SWL_CODE = "SWL"; +string const TIME_SERIES_SWL_NAME = "SWL"; + +// CShore stuff +string const WAVE_ENERGY_FLUX = "wave_energy_flux"; +string const WAVE_HEIGHT_X_FILENAME = "wave_height_x.csv"; +string const WAVE_HEIGHT_Y_FILENAME = "wave_height_y.csv"; +string const ACTIVE_ZONE_FILENAME = "activezone.csv"; + +//================================================ Globally-available functions ================================================= +template +T tMax(T a, T b) +{ + return ((a > b) ? a : b); +} + +template +T tMax(T a, T b, T c) +{ + T max = (a < b) ? b : a; + return ((max < c) ? c : max); +} + +template +T tMin(T a, T b) +{ + return ((a < b) ? a : b); +} + +template +T tMin(T a, T b, T c) +{ + return (a < b ? (a < c ? a : c) : (b < c ? b : c)); +} + +template +T tAbs(T a) +{ + // From a posting dated 18 Nov 93 by rmartin@rcmcon.com (Robert Martin), archived in cpp_tips + return ((a < 0) ? -a : a); +} + +// template +// bool bIsBetween(T a, T b, T c) +// { +// // Assumes b > c +// return ((a >= b) && (a <= c)); +// } + +template +string strDblToStr(const T &t) +{ + // From http://stackoverflow.com/questions/2125880/convert-float-to-stdstring-in-c + ostringstream os; + os << t; + return os.str(); +} + +// ============================================================================================================================== +// For comparison of two floating-point numbers, with a specified accuracy +// ============================================================================================================================== +template +bool bFPIsEqual(const T d1, const T d2, const T dEpsilon) +{ + // Since the accuracy of floating-point numbers varies with their magnitude, we must compare them by using an accuracy threshold which is relative to the magnitude of the two numbers being compared. This is a blend of an example from Knuth's 'The Art of Computer Programming. Volume 1. Fundamental Algorithms' and a posting dated 18 Nov 93 by rmartin@rcmcon.com (Robert Martin), archived in cpp_tips + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + + if ((0 == d1) && (tAbs(d2) < dEpsilon)) + return true; + else if ((0 == d2) && (tAbs(d1) < dEpsilon)) + return true; + else + return ((tAbs(d1 - d2) < (dEpsilon * tAbs(d1))) ? true : false); + +#pragma GCC diagnostic pop +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Definitions are in utilsglobal.cpp +double dRound(double const); +int nRound(double const); +// bool bIsWhole(double const); +bool bIsStringValidDouble(string &); +bool bIsStringValidInt(string &); + +struct FillToWidth +{ + FillToWidth(char f, int w) : chFill(f), nWidth(w) {} + char chFill; + int nWidth; +}; + +//! Struct to hold wave data for a single transect/profile +struct TransectWaveData +{ + std::vector VdX; + std::vector VdY; + std::vector VdHeightX; + std::vector VdHeightY; + std::vector VbBreaking; + int nCoastID; + int nProfileID; + bool bIsGridEdge; +}; + +ostream &operator<<(ostream &, const FillToWidth &); + +// string strDbl(double const, int const); +string strDblRight(double const, int const, int const, bool const = true); +string strIntRight(int const, int const); +string strCentre(const char *, int const); +string strCentre(const string &, int const); +string strRight(const string &, int const); +string strRight(const char *, int const); +string strLeft(const string &, int const); +string strLeft(const char *, int const); +string strRightPerCent(double const, double const, int const, int const, + bool const = true); +#endif + +//================================================= debugging stuff ============================================================= +// #define CLOCKCHECK // Uncomment to check CPU clock rollover settings + +#endif // CME_H diff --git a/src/configuration.cpp b/src/configuration.cpp index 219f5c76c..01b60b775 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -1,458 +1,458 @@ -/*! - - \file configuration.cpp - \brief Implementation of unified configuration class for CoastalME - \details Provides default values and initialization for simulation parameters - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License - -*/ - -/* ============================================================================================================================== - - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -==============================================================================================================================*/ -#include "cme.h" -#include "configuration.h" -// #include "simulation.h" -#include -#include -#include - -//=============================================================================================================================== -//! Constructor -//=============================================================================================================================== -CConfiguration::CConfiguration() -{ - InitializeDefaults(); -} - -//=============================================================================================================================== -//! Destructor -//=============================================================================================================================== -CConfiguration::~CConfiguration() -{ -} - -//=============================================================================================================================== -//! Initialize all parameters with default values -//=============================================================================================================================== -void CConfiguration::InitializeDefaults() -{ - // Run Information - m_strRunName = "cme"; - m_nLogFileDetail = 1; - m_bCSVPerTimestepResults = true; - - // Simulation timing - m_strStartDateTime = "00-00-00 01/01/2000"; - m_strDuration = "1 hour"; - m_strTimestep = "1 hour"; - m_vecSaveTimes.clear(); - m_nRandomSeed = 0; - m_bUseSystemTimeForSeed = true; - - // GIS Output - m_nMaxSaveDigits = 3; - m_strSaveDigitsMode = "sequential"; - m_vecRasterFiles.clear(); - m_vecRasterFiles.push_back(""); - m_strRasterFormat = ""; - m_bWorldFile = false; - m_bScaleValues = false; - m_vecSliceElevations.clear(); - m_vecVectorFiles.clear(); - m_vecVectorFiles.push_back(""); - m_strVectorFormat = "ESRI Shapefile"; - m_vecTimeSeriesFiles.clear(); - m_vecTimeSeriesFiles.push_back(""); - - // Grid and Coastline - m_nCoastlineSmoothing = 0; - m_nCoastlineSmoothingWindow = 7; - m_nPolynomialOrder = 4; - m_strOmitGridEdges = ""; - m_nProfileSmoothingWindow = 0; - m_dMaxLocalSlope = 1.0; - m_dMaxBeachElevation = 10.0; - - // Layers and Files - m_nNumLayers = 1; - m_strBasementDEMFile = ""; - m_vecUnconsFineFiles.clear(); - m_vecUnconsSandFiles.clear(); - m_vecUnconsCoarseFiles.clear(); - m_vecConsFineFiles.clear(); - m_vecConsSandFiles.clear(); - m_vecConsCoarseFiles.clear(); - m_strSuspendedSedFile = ""; - m_strLandformFile = ""; - m_strInterventionClassFile = ""; - m_strInterventionHeightFile = ""; - - // Hydrology - m_nWavePropagationModel = 1; // CShore - m_dSeawaterDensity = 1029.0; - m_dInitialWaterLevel = 0.0; - // m_dFinalWaterLevel = 0.0; - m_bHasFinalWaterLevel = false; - - m_strWaveHeightTimeSeries = ""; - m_strWaveStationDataFile = ""; - m_dDeepWaterWaveHeight = 1.0; - m_dDeepWaterWaveOrientation = 270.0; - m_dWavePeriod = 10.0; - - m_strTideDataFile = ""; - m_dBreakingWaveRatio = 0.8; - - // Sediment and Erosion - m_bCoastPlatformErosion = true; - m_dPlatformErosionResistance = 2e6; - m_bBeachSedimentTransport = true; - m_nBeachTransportAtEdges = 1; // open - m_nBeachErosionEquation = 0; // CERC - m_dFineMedianSize = 0.0; - m_dSandMedianSize = 0.0; - m_dCoarseMedianSize = 0.0; - m_dSedimentDensity = 2650.0; - m_dBeachSedimentPorosity = 0.4; - m_dFineErosivity = 1.0; - m_dSandErosivity = 0.7; - m_dCoarseErosivity = 0.3; - m_dTransportKLS = 0.4; - m_dKamphuis = 5.0; - m_dBermHeight = 0.25; - - // Cliff parameters - m_bCliffCollapse = true; - m_dCliffErosionResistance = 2.5e8; - m_dNotchOverhang = 0.5; - m_dNotchBase = 0.3; - m_dCliffDepositionA = 0.0; - m_dTalusWidth = 15.0; - m_dMinTalusLength = 10.0; - m_dMinTalusHeight = 0.5; - - // Flood parameters - m_bFloodInput = false; - m_strFloodCoastline = ""; - m_strRunupEquation = 0; - m_strFloodLocations = ""; - m_strFloodInputLocation = ""; - - // Sediment input parameters - m_bSedimentInput = false; - m_strSedimentInputLocation = ""; - m_strSedimentInputType = ""; - m_strSedimentInputDetails = ""; - - // Physics and Geometry - m_dGravitationalAcceleration = 9.81; - m_dNormalSpacing = 0.0; - m_dRandomFactor = 0.25; - m_dNormalLength = 130.0; - m_dStartDepthRatio = 30.0; - m_dSyntheticTransectSpacing = 5.0; - - // Profile and Output Options - m_bSaveProfileData = false; - m_vecProfileNumbers.clear(); - m_vecProfileTimesteps.clear(); - m_bSaveParallelProfiles = false; - m_bOutputErosionPotential = false; - m_nCurvatureWindow = 11; - - // Cliff Edge Processing - m_nCliffEdgeSmoothing = 1; - m_nCliffEdgeSmoothingWindow = 33; - m_nCliffEdgePolynomialOrder = 4; - m_dCliffSlopeLimit = 0.3; -} - -//=============================================================================================================================== -//! Get raster files with keyword expansion support -//=============================================================================================================================== -vector CConfiguration::GetRasterFiles() const -{ - // Case 11: Raster GIS files to output - expand "all" and "usual" keywords - vector expandedFiles; - - for (string const &fileSpec : m_vecRasterFiles) - { - string fileSpecLower = fileSpec; - std::transform(fileSpecLower.begin(), fileSpecLower.end(), - fileSpecLower.begin(), ::tolower); - - if (fileSpecLower == "all") - { - // Add all possible raster outputs (Case 11 "all" mode) - expandedFiles.insert(expandedFiles.end(), - {"suspended_sediment", - "avg_suspended_sediment", - "fine_uncons", - "fine_cons", - "sand_uncons", - "sand_cons", - "coarse_uncons", - "coarse_cons", - "sediment_top_elevation", - "top_elevation", - "sea_depth", - "wave_height", - "wave_orientation", - "wave_period", - "potential_platform_erosion", - "actual_platform_erosion", - "total_potential_platform_erosion", - "total_actual_platform_erosion", - "potential_beach_erosion", - "actual_beach_erosion", - "total_potential_beach_erosion", - "total_actual_beach_erosion", - "beach_deposition", - "total_beach_deposition", - "landform", - "local_cons_sediment_slope", - "slope", - "cliff", - "avg_sea_depth", - "avg_wave_height", - "avg_wave_orientation", - "beach_protection", - "basement_elevation", - "coastline", - "coast_normal", - "active_zone", - "cliff_collapse", - "total_cliff_collapse", - "cliff_collapse_deposition", - "total_cliff_collapse_deposition", - "polygon", - "potential_platform_erosion_mask", - "sea_mask", - "beach_mask", - "shadow_zone_codes", - "deep_water_wave_angle", - "deep_water_wave_height", - "deep_water_wave_period", - "polygon_uncons_sediment_up_or_down_drift", - "polygon_uncons_sediment_gain_or_loss"}); - } - else if (fileSpecLower == "usual") - { - // Add usual/standard raster outputs (Case 11 "usual" mode) - expandedFiles.insert(expandedFiles.end(), - {"suspended_sediment", - "avg_suspended_sediment", - "fine_uncons", - "fine_cons", - "sand_uncons", - "sand_cons", - "coarse_uncons", - "coarse_cons", - "sediment_top_elevation", - "top_elevation", - "sea_depth", - "wave_height", - "wave_orientation", - "potential_platform_erosion", - "actual_platform_erosion", - "total_potential_platform_erosion", - "total_actual_platform_erosion", - "potential_beach_erosion", - "actual_beach_erosion", - "total_potential_beach_erosion", - "total_actual_beach_erosion", - "beach_deposition", - "total_beach_deposition", - "landform", - "local_cons_sediment_slope", - "slope", - "avg_wave_height", - "avg_wave_orientation", - "beach_protection", - "basement_elevation", - "active_zone", - "cliff_collapse", - "total_cliff_collapse", - "cliff_collapse_deposition", - "total_cliff_collapse_deposition", - "polygon", - "shadow_zone_codes", - "polygon_uncons_sediment_up_or_down_drift", - "polygon_uncons_sediment_gain_or_loss", - "coast_normal", - "coastline", - "fine_uncons", - "fine_cons", - "sand_uncons", - "sand_cons", - "coarse_uncons", - "coarse_cons"}); - } - else if (fileSpecLower == "cmetools") - { - // Add usual/standard raster outputs (Case 11 "usual" mode) - expandedFiles.insert(expandedFiles.end(), - {"fine_uncons", - "fine_cons", - "sand_uncons", - "sand_cons", - "coarse_uncons", - "coarse_cons", - "top_elevation", - "sea_depth", - "wave_height", - "total_actual_platform_erosion", - "total_actual_beach_erosion", - "total_beach_deposition", - "landform", - "basement_elevation", - "active_zone", - "cliff_collapse", - "total_cliff_collapse", - "cliff_collapse_deposition", - "total_cliff_collapse_deposition", - "polygon", - "coast_normal", - "coastline"}); - } - else if (fileSpecLower == "" or fileSpecLower == "none") - { - return expandedFiles; - } - else - { - // Regular file specification - add as-is - expandedFiles.push_back(fileSpec); - } - } - - return expandedFiles; -} - -//=============================================================================================================================== -//! Get vector files with keyword expansion support -//=============================================================================================================================== -vector CConfiguration::GetVectorFiles() const -{ - // Case 16: Vector GIS files to output - expand "all" and "usual" keywords - vector expandedFiles; - - for (string const &fileSpec : m_vecVectorFiles) - { - string fileSpecLower = fileSpec; - std::transform(fileSpecLower.begin(), fileSpecLower.end(), - fileSpecLower.begin(), ::tolower); - - if (fileSpecLower == "all") - { - // Add all possible vector outputs (Case 16 "all" mode) - expandedFiles.insert(expandedFiles.end(), {"coast", "cliff_edge", "wave_angle", "normals", "invalid_normals", "avg_wave_angle", "wave_energy", "mean_wave_energy", "breaking_wave_height", "coast_curvature", "polygon_node", "polygon", "cliff_notch", "wave_transect_points", "shadow_boundary", "downdrift_boundary", "deep_water_wave_angle", "wave_setup", "storm_surge", "run_up", "flood_line"}); - } - else if (fileSpecLower == "usual") - { - // Add usual/standard vector outputs (Case 16 "usual" mode) - expandedFiles.insert( - expandedFiles.end(), - {"coast", "cliff_edge", "wave_angle", "normals", "invalid_normals", - "avg_wave_angle", "wave_energy", "mean_wave_energy", - "breaking_wave_height", "polygon", "cliff_notch", - "shadow_boundary", "downdrift_boundary", "deep_water_wave_angle"}); - } - else if (fileSpecLower == "" or fileSpecLower == "none") - { - return expandedFiles; - } - else - { - // Regular file specification - add as-is - expandedFiles.push_back(fileSpec); - } - } - - return expandedFiles; -} -//=============================================================================================================================== - -//! Get time series files with keyword expansion support -//=============================================================================================================================== -vector CConfiguration::GetTimeSeriesFiles() const -{ - // Case 18: Timeseries files to output - expand "all" and "usual" keywords - vector expandedFiles; - - for (string const &fileSpec : m_vecVectorFiles) - { - string fileSpecLower = fileSpec; - std::transform(fileSpecLower.begin(), fileSpecLower.end(), - fileSpecLower.begin(), ::tolower); - - if (fileSpecLower == "all") - { - // Add all possible vector outputs (Case 16 "all" mode) - expandedFiles.insert(expandedFiles.end(), {"wave_setup", "wave_runup", "beach_change_net", "beach_deposition", "beach_erosion", "cliff_collapse_deposition", "cliff_collapse_erosion", "cliff_collapse_net", "platform_erosion", "sea_area", "suspended", "water_level"}); - } - else if (fileSpecLower == "" or fileSpecLower == "none") - { - return expandedFiles; - } - else - { - // Regular file specification - add as-is - expandedFiles.push_back(fileSpec); - } - } - - return expandedFiles; -} - -//! Get time series files with keyword expansion support -//=============================================================================================================================== -vector CConfiguration::GetFloodFiles() const -{ - // Case 18: Timeseries files to output - expand "all" and "usual" keywords - vector expandedFiles; - - for (string const &fileSpec : m_vecVectorFiles) - { - string fileSpecLower = fileSpec; - std::transform(fileSpecLower.begin(), fileSpecLower.end(), - fileSpecLower.begin(), ::tolower); - - if (fileSpecLower == "all") - { - // Add all possible vector outputs (Case 16 "all" mode) - expandedFiles.insert(expandedFiles.end(), {}); - } - else if (fileSpecLower == "" or fileSpecLower == "none") - { - return expandedFiles; - } - else - { - // Regular file specification - add as-is - expandedFiles.push_back(fileSpec); - } - } - - return expandedFiles; -} - -string CConfiguration::GetOmitGridEdges() const -{ - // This needs to be lower case - std::string my_text{m_strOmitGridEdges}; - std::transform(my_text.begin(), my_text.end(), my_text.begin(), ::tolower); - return my_text; -} +/*! + + \file configuration.cpp + \brief Implementation of unified configuration class for CoastalME + \details Provides default values and initialization for simulation parameters + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License + +*/ + +/* ============================================================================================================================== + + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +==============================================================================================================================*/ +#include "cme.h" +#include "configuration.h" +// #include "simulation.h" +#include +#include +#include + +//=============================================================================================================================== +//! Constructor +//=============================================================================================================================== +CConfiguration::CConfiguration() +{ + InitializeDefaults(); +} + +//=============================================================================================================================== +//! Destructor +//=============================================================================================================================== +CConfiguration::~CConfiguration() +{ +} + +//=============================================================================================================================== +//! Initialize all parameters with default values +//=============================================================================================================================== +void CConfiguration::InitializeDefaults() +{ + // Run Information + m_strRunName = "cme"; + m_nLogFileDetail = 1; + m_bCSVPerTimestepResults = true; + + // Simulation timing + m_strStartDateTime = "00-00-00 01/01/2000"; + m_strDuration = "1 hour"; + m_strTimestep = "1 hour"; + m_vecSaveTimes.clear(); + m_nRandomSeed = 0; + m_bUseSystemTimeForSeed = true; + + // GIS Output + m_nMaxSaveDigits = 3; + m_strSaveDigitsMode = "sequential"; + m_vecRasterFiles.clear(); + m_vecRasterFiles.push_back(""); + m_strRasterFormat = ""; + m_bWorldFile = false; + m_bScaleValues = false; + m_vecSliceElevations.clear(); + m_vecVectorFiles.clear(); + m_vecVectorFiles.push_back(""); + m_strVectorFormat = "ESRI Shapefile"; + m_vecTimeSeriesFiles.clear(); + m_vecTimeSeriesFiles.push_back(""); + + // Grid and Coastline + m_nCoastlineSmoothing = 0; + m_nCoastlineSmoothingWindow = 7; + m_nPolynomialOrder = 4; + m_strOmitGridEdges = ""; + m_nProfileSmoothingWindow = 0; + m_dMaxLocalSlope = 1.0; + m_dMaxBeachElevation = 10.0; + + // Layers and Files + m_nNumLayers = 1; + m_strBasementDEMFile = ""; + m_vecUnconsFineFiles.clear(); + m_vecUnconsSandFiles.clear(); + m_vecUnconsCoarseFiles.clear(); + m_vecConsFineFiles.clear(); + m_vecConsSandFiles.clear(); + m_vecConsCoarseFiles.clear(); + m_strSuspendedSedFile = ""; + m_strLandformFile = ""; + m_strInterventionClassFile = ""; + m_strInterventionHeightFile = ""; + + // Hydrology + m_nWavePropagationModel = 1; // CShore + m_dSeawaterDensity = 1029.0; + m_dInitialWaterLevel = 0.0; + // m_dFinalWaterLevel = 0.0; + m_bHasFinalWaterLevel = false; + + m_strWaveHeightTimeSeries = ""; + m_strWaveStationDataFile = ""; + m_dDeepWaterWaveHeight = 1.0; + m_dDeepWaterWaveOrientation = 270.0; + m_dWavePeriod = 10.0; + + m_strTideDataFile = ""; + m_dBreakingWaveRatio = 0.8; + + // Sediment and Erosion + m_bCoastPlatformErosion = true; + m_dPlatformErosionResistance = 2e6; + m_bBeachSedimentTransport = true; + m_nBeachTransportAtEdges = 1; // open + m_nBeachErosionEquation = 0; // CERC + m_dFineMedianSize = 0.0; + m_dSandMedianSize = 0.0; + m_dCoarseMedianSize = 0.0; + m_dSedimentDensity = 2650.0; + m_dBeachSedimentPorosity = 0.4; + m_dFineErosivity = 1.0; + m_dSandErosivity = 0.7; + m_dCoarseErosivity = 0.3; + m_dTransportKLS = 0.4; + m_dKamphuis = 5.0; + m_dBermHeight = 0.25; + + // Cliff parameters + m_bCliffCollapse = true; + m_dCliffErosionResistance = 2.5e8; + m_dNotchOverhang = 0.5; + m_dNotchBase = 0.3; + m_dCliffDepositionA = 0.0; + m_dTalusWidth = 15.0; + m_dMinTalusLength = 10.0; + m_dMinTalusHeight = 0.5; + + // Flood parameters + m_bFloodInput = false; + m_strFloodCoastline = ""; + m_strRunupEquation = 0; + m_strFloodLocations = ""; + m_strFloodInputLocation = ""; + + // Sediment input parameters + m_bSedimentInput = false; + m_strSedimentInputLocation = ""; + m_strSedimentInputType = ""; + m_strSedimentInputDetails = ""; + + // Physics and Geometry + m_dGravitationalAcceleration = 9.81; + m_dNormalSpacing = 0.0; + m_dRandomFactor = 0.25; + m_dNormalLength = 130.0; + m_dStartDepthRatio = 30.0; + m_dSyntheticTransectSpacing = 5.0; + + // Profile and Output Options + m_bSaveProfileData = false; + m_vecProfileNumbers.clear(); + m_vecProfileTimesteps.clear(); + m_bSaveParallelProfiles = false; + m_bOutputErosionPotential = false; + m_nCurvatureWindow = 11; + + // Cliff Edge Processing + m_nCliffEdgeSmoothing = 1; + m_nCliffEdgeSmoothingWindow = 33; + m_nCliffEdgePolynomialOrder = 4; + m_dCliffSlopeLimit = 0.3; +} + +//=============================================================================================================================== +//! Get raster files with keyword expansion support +//=============================================================================================================================== +vector CConfiguration::GetRasterFiles() const +{ + // Case 11: Raster GIS files to output - expand "all" and "usual" keywords + vector expandedFiles; + + for (string const &fileSpec : m_vecRasterFiles) + { + string fileSpecLower = fileSpec; + std::transform(fileSpecLower.begin(), fileSpecLower.end(), + fileSpecLower.begin(), ::tolower); + + if (fileSpecLower == "all") + { + // Add all possible raster outputs (Case 11 "all" mode) + expandedFiles.insert(expandedFiles.end(), + {"suspended_sediment", + "avg_suspended_sediment", + "fine_uncons", + "fine_cons", + "sand_uncons", + "sand_cons", + "coarse_uncons", + "coarse_cons", + "sediment_top_elevation", + "top_elevation", + "sea_depth", + "wave_height", + "wave_orientation", + "wave_period", + "potential_platform_erosion", + "actual_platform_erosion", + "total_potential_platform_erosion", + "total_actual_platform_erosion", + "potential_beach_erosion", + "actual_beach_erosion", + "total_potential_beach_erosion", + "total_actual_beach_erosion", + "beach_deposition", + "total_beach_deposition", + "landform", + "local_cons_sediment_slope", + "slope", + "cliff", + "avg_sea_depth", + "avg_wave_height", + "avg_wave_orientation", + "beach_protection", + "basement_elevation", + "coastline", + "coast_normal", + "active_zone", + "cliff_collapse", + "total_cliff_collapse", + "cliff_collapse_deposition", + "total_cliff_collapse_deposition", + "polygon", + "potential_platform_erosion_mask", + "sea_mask", + "beach_mask", + "shadow_zone_codes", + "deep_water_wave_angle", + "deep_water_wave_height", + "deep_water_wave_period", + "polygon_uncons_sediment_up_or_down_drift", + "polygon_uncons_sediment_gain_or_loss"}); + } + else if (fileSpecLower == "usual") + { + // Add usual/standard raster outputs (Case 11 "usual" mode) + expandedFiles.insert(expandedFiles.end(), + {"suspended_sediment", + "avg_suspended_sediment", + "fine_uncons", + "fine_cons", + "sand_uncons", + "sand_cons", + "coarse_uncons", + "coarse_cons", + "sediment_top_elevation", + "top_elevation", + "sea_depth", + "wave_height", + "wave_orientation", + "potential_platform_erosion", + "actual_platform_erosion", + "total_potential_platform_erosion", + "total_actual_platform_erosion", + "potential_beach_erosion", + "actual_beach_erosion", + "total_potential_beach_erosion", + "total_actual_beach_erosion", + "beach_deposition", + "total_beach_deposition", + "landform", + "local_cons_sediment_slope", + "slope", + "avg_wave_height", + "avg_wave_orientation", + "beach_protection", + "basement_elevation", + "active_zone", + "cliff_collapse", + "total_cliff_collapse", + "cliff_collapse_deposition", + "total_cliff_collapse_deposition", + "polygon", + "shadow_zone_codes", + "polygon_uncons_sediment_up_or_down_drift", + "polygon_uncons_sediment_gain_or_loss", + "coast_normal", + "coastline", + "fine_uncons", + "fine_cons", + "sand_uncons", + "sand_cons", + "coarse_uncons", + "coarse_cons"}); + } + else if (fileSpecLower == "cmetools") + { + // Add usual/standard raster outputs (Case 11 "usual" mode) + expandedFiles.insert(expandedFiles.end(), + {"fine_uncons", + "fine_cons", + "sand_uncons", + "sand_cons", + "coarse_uncons", + "coarse_cons", + "top_elevation", + "sea_depth", + "wave_height", + "total_actual_platform_erosion", + "total_actual_beach_erosion", + "total_beach_deposition", + "landform", + "basement_elevation", + "active_zone", + "cliff_collapse", + "total_cliff_collapse", + "cliff_collapse_deposition", + "total_cliff_collapse_deposition", + "polygon", + "coast_normal", + "coastline"}); + } + else if (fileSpecLower == "" or fileSpecLower == "none") + { + return expandedFiles; + } + else + { + // Regular file specification - add as-is + expandedFiles.push_back(fileSpec); + } + } + + return expandedFiles; +} + +//=============================================================================================================================== +//! Get vector files with keyword expansion support +//=============================================================================================================================== +vector CConfiguration::GetVectorFiles() const +{ + // Case 16: Vector GIS files to output - expand "all" and "usual" keywords + vector expandedFiles; + + for (string const &fileSpec : m_vecVectorFiles) + { + string fileSpecLower = fileSpec; + std::transform(fileSpecLower.begin(), fileSpecLower.end(), + fileSpecLower.begin(), ::tolower); + + if (fileSpecLower == "all") + { + // Add all possible vector outputs (Case 16 "all" mode) + expandedFiles.insert(expandedFiles.end(), {"coast", "cliff_edge", "wave_angle", "normals", "invalid_normals", "avg_wave_angle", "wave_energy", "mean_wave_energy", "breaking_wave_height", "coast_curvature", "polygon_node", "polygon", "cliff_notch", "wave_transect_points", "shadow_boundary", "downdrift_boundary", "deep_water_wave_angle", "wave_setup", "storm_surge", "run_up", "flood_line"}); + } + else if (fileSpecLower == "usual") + { + // Add usual/standard vector outputs (Case 16 "usual" mode) + expandedFiles.insert( + expandedFiles.end(), + {"coast", "cliff_edge", "wave_angle", "normals", "invalid_normals", + "avg_wave_angle", "wave_energy", "mean_wave_energy", + "breaking_wave_height", "polygon", "cliff_notch", + "shadow_boundary", "downdrift_boundary", "deep_water_wave_angle"}); + } + else if (fileSpecLower == "" or fileSpecLower == "none") + { + return expandedFiles; + } + else + { + // Regular file specification - add as-is + expandedFiles.push_back(fileSpec); + } + } + + return expandedFiles; +} +//=============================================================================================================================== + +//! Get time series files with keyword expansion support +//=============================================================================================================================== +vector CConfiguration::GetTimeSeriesFiles() const +{ + // Case 18: Timeseries files to output - expand "all" and "usual" keywords + vector expandedFiles; + + for (string const &fileSpec : m_vecVectorFiles) + { + string fileSpecLower = fileSpec; + std::transform(fileSpecLower.begin(), fileSpecLower.end(), + fileSpecLower.begin(), ::tolower); + + if (fileSpecLower == "all") + { + // Add all possible vector outputs (Case 16 "all" mode) + expandedFiles.insert(expandedFiles.end(), {"wave_setup", "wave_runup", "beach_change_net", "beach_deposition", "beach_erosion", "cliff_collapse_deposition", "cliff_collapse_erosion", "cliff_collapse_net", "platform_erosion", "sea_area", "suspended", "water_level"}); + } + else if (fileSpecLower == "" or fileSpecLower == "none") + { + return expandedFiles; + } + else + { + // Regular file specification - add as-is + expandedFiles.push_back(fileSpec); + } + } + + return expandedFiles; +} + +//! Get time series files with keyword expansion support +//=============================================================================================================================== +vector CConfiguration::GetFloodFiles() const +{ + // Case 18: Timeseries files to output - expand "all" and "usual" keywords + vector expandedFiles; + + for (string const &fileSpec : m_vecVectorFiles) + { + string fileSpecLower = fileSpec; + std::transform(fileSpecLower.begin(), fileSpecLower.end(), + fileSpecLower.begin(), ::tolower); + + if (fileSpecLower == "all") + { + // Add all possible vector outputs (Case 16 "all" mode) + expandedFiles.insert(expandedFiles.end(), {}); + } + else if (fileSpecLower == "" or fileSpecLower == "none") + { + return expandedFiles; + } + else + { + // Regular file specification - add as-is + expandedFiles.push_back(fileSpec); + } + } + + return expandedFiles; +} + +string CConfiguration::GetOmitGridEdges() const +{ + // This needs to be lower case + std::string my_text{m_strOmitGridEdges}; + std::transform(my_text.begin(), my_text.end(), my_text.begin(), ::tolower); + return my_text; +} diff --git a/src/configuration.h b/src/configuration.h index e63ad2fed..30469722b 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -1,1013 +1,1013 @@ -/*! - - \file configuration.h - \brief Unified configuration class for CoastalME simulation parameters - \details Provides a single interface for accessing simulation parameters - regardless of input format (.dat or YAML) - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License - -*/ - -/* ============================================================================================================================== - - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - - This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. - -==============================================================================================================================*/ -#ifndef CONFIGURATION_H -#define CONFIGURATION_H - -#include -#include -#include -#include - -using std::string; -using std::vector; - -//! Unified configuration class for CoastalME simulation parameters -class CConfiguration -{ - private: - // Run Information - string m_strRunName; - int m_nLogFileDetail; - bool m_bCSVPerTimestepResults; - - // Simulation timing - string m_strStartDateTime; - string m_strDuration; - string m_strTimestep; - vector m_vecSaveTimes; - int m_nRandomSeed; - bool m_bUseSystemTimeForSeed; - - // GIS Output - int m_nMaxSaveDigits; - string m_strSaveDigitsMode; - vector m_vecRasterFiles; - string m_strRasterFormat; - bool m_bWorldFile; - bool m_bScaleValues; - vector m_vecSliceElevations; - vector m_vecVectorFiles; - string m_strVectorFormat; - vector m_vecTimeSeriesFiles; - - // Grid and Coastline - int m_nCoastlineSmoothing; - int m_nCoastlineSmoothingWindow; - int m_nPolynomialOrder; - string m_strOmitGridEdges; - int m_nProfileSmoothingWindow; - double m_dMaxLocalSlope; - double m_dMaxBeachElevation; - - // Layers and Files - int m_nNumLayers; - string m_strBasementDEMFile; - vector m_vecUnconsFineFiles; - vector m_vecUnconsSandFiles; - vector m_vecUnconsCoarseFiles; - vector m_vecConsFineFiles; - vector m_vecConsSandFiles; - vector m_vecConsCoarseFiles; - string m_strSuspendedSedFile; - string m_strLandformFile; - string m_strInterventionClassFile; - string m_strInterventionHeightFile; - - // Hydrology - int m_nWavePropagationModel; - double m_dSeawaterDensity; - double m_dInitialWaterLevel; - double m_dFinalWaterLevel; - bool m_bHasFinalWaterLevel; - - // Waves - string m_strWaveHeightTimeSeries; - string m_strWaveStationDataFile; - double m_dDeepWaterWaveHeight; - double m_dDeepWaterWaveOrientation; - double m_dWavePeriod; - - // tides - string m_strTideDataFile; - double m_dBreakingWaveRatio; - - // Sediment and Erosion - bool m_bCoastPlatformErosion; - double m_dPlatformErosionResistance; - bool m_bBeachSedimentTransport; - int m_nBeachTransportAtEdges; - int m_nBeachErosionEquation; - double m_dFineMedianSize; - double m_dSandMedianSize; - double m_dCoarseMedianSize; - double m_dSedimentDensity; - double m_dBeachSedimentPorosity; - double m_dFineErosivity; - double m_dSandErosivity; - double m_dCoarseErosivity; - double m_dTransportKLS; - double m_dKamphuis; - double m_dBermHeight; - - // Cliff parameters - bool m_bCliffCollapse; - double m_dCliffErosionResistance; - double m_dNotchOverhang; - double m_dNotchBase; - double m_dCliffDepositionA; - double m_dTalusWidth; - double m_dMinTalusLength; - double m_dMinTalusHeight; - - // Flood parameters - bool m_bFloodInput; - string m_strFloodCoastline; - int m_strRunupEquation; - string m_strFloodLocations; - string m_strFloodInputLocation; - vector m_vecFloodFiles; - - // Sediment input parameters - bool m_bSedimentInput; - string m_strSedimentInputLocation; - string m_strSedimentInputType; - string m_strSedimentInputDetails; - - // Physics and Geometry - double m_dGravitationalAcceleration; - double m_dNormalSpacing; - double m_dRandomFactor; - double m_dNormalLength; - double m_dStartDepthRatio; - double m_dSyntheticTransectSpacing; - - // Profile and Output Options - bool m_bSaveProfileData; - vector m_vecProfileNumbers; - vector m_vecProfileTimesteps; - bool m_bSaveParallelProfiles; - bool m_bOutputErosionPotential; - int m_nCurvatureWindow; - - // Cliff Edge Processing - int m_nCliffEdgeSmoothing; - int m_nCliffEdgeSmoothingWindow; - int m_nCliffEdgePolynomialOrder; - double m_dCliffSlopeLimit; - - public: - CConfiguration(); - ~CConfiguration(); - - // Setters for all parameters - void SetRunName(string const &str) - { - m_strRunName = str; - } - void SetLogFileDetail(int n) - { - m_nLogFileDetail = n; - } - void SetCSVPerTimestepResults(bool b) - { - m_bCSVPerTimestepResults = b; - } - void SetStartDateTime(string const &str) - { - m_strStartDateTime = str; - } - void SetDuration(string const &str) - { - m_strDuration = str; - } - void SetTimestep(string const &str) - { - m_strTimestep = str; - } - void SetSaveTimes(vector const &vec) - { - m_vecSaveTimes = vec; - } - void SetRandomSeed(int n) - { - m_nRandomSeed = n; - m_bUseSystemTimeForSeed = false; - } - void UseSystemTimeForSeed() - { - m_bUseSystemTimeForSeed = false; - } - - void SetMaxSaveDigits(int n) - { - m_nMaxSaveDigits = n; - } - void SetSaveDigitsMode(string const &str) - { - m_strSaveDigitsMode = str; - } - void SetRasterFiles(vector const &vec) - { - m_vecRasterFiles = vec; - } - void SetRasterFormat(string const &str) - { - m_strRasterFormat = str; - } - void SetWorldFile(bool b) - { - m_bWorldFile = b; - } - void SetScaleValues(bool b) - { - m_bScaleValues = b; - } - void SetSliceElevations(vector const &vec) - { - m_vecSliceElevations = vec; - } - void SetVectorFiles(vector const &vec) - { - m_vecVectorFiles = vec; - } - void SetVectorFormat(string const &str) - { - m_strVectorFormat = str; - } - void SetTimeSeriesFiles(vector const &vec) - { - m_vecTimeSeriesFiles = vec; - } - - void SetCoastlineSmoothing(int n) - { - m_nCoastlineSmoothing = n; - } - void SetCoastlineSmoothingWindow(int n) - { - m_nCoastlineSmoothingWindow = n; - } - void SetPolynomialOrder(int n) - { - m_nPolynomialOrder = n; - } - void SetOmitGridEdges(string const &str) - { - m_strOmitGridEdges = str; - } - void SetProfileSmoothingWindow(int n) - { - m_nProfileSmoothingWindow = n; - } - void SetMaxLocalSlope(double d) - { - m_dMaxLocalSlope = d; - } - void SetMaxBeachElevation(double d) - { - m_dMaxBeachElevation = d; - } - void SetNumLayers(int n) - { - m_nNumLayers = n; - } - void SetBasementDEMFile(string const &str) - { - m_strBasementDEMFile = str; - } - void SetUnconsFineFiles(vector const &vec) - { - m_vecUnconsFineFiles = vec; - } - void SetUnconsSandFiles(vector const &vec) - { - m_vecUnconsSandFiles = vec; - } - void SetUnconsCoarseFiles(vector const &vec) - { - m_vecUnconsCoarseFiles = vec; - } - void SetConsFineFiles(vector const &vec) - { - m_vecConsFineFiles = vec; - } - void SetConsSandFiles(vector const &vec) - { - m_vecConsSandFiles = vec; - } - void SetConsCoarseFiles(vector const &vec) - { - m_vecConsCoarseFiles = vec; - } - void SetSuspendedSedFile(string const &str) - { - m_strSuspendedSedFile = str; - } - void SetLandformFile(string const &str) - { - m_strLandformFile = str; - } - void SetInterventionClassFile(string const &str) - { - m_strInterventionClassFile = str; - } - void SetInterventionHeightFile(string const &str) - { - m_strInterventionHeightFile = str; - } - - void SetWavePropagationModel(int n) - { - m_nWavePropagationModel = n; - } - void SetSeawaterDensity(double d) - { - m_dSeawaterDensity = d; - } - void SetInitialWaterLevel(double d) - { - m_dInitialWaterLevel = d; - } - void SetFinalWaterLevel(double d) - { - m_dFinalWaterLevel = d; - m_bHasFinalWaterLevel = true; - } - - // Wave height Data - void SetWaveHeightTimeSeries(string const &str) - { - m_strWaveHeightTimeSeries = str; - } - void SetWaveStationDataFile(string const &str) - { - m_strWaveStationDataFile = str; - } - void SetDeepWaterWaveHeight(double d) - { - m_dDeepWaterWaveHeight = d; - } - void SetDeepWaterWaveOrientation(double d) - { - m_dDeepWaterWaveOrientation = d; - } - void SetWavePeriod(double d) - { - m_dWavePeriod = d; - } - - void SetTideDataFile(string const &str) - { - m_strTideDataFile = str; - } - void SetBreakingWaveRatio(double d) - { - m_dBreakingWaveRatio = d; - } - - // Additional setters for comprehensive YAML support - void SetCoastPlatformErosion(bool b) - { - m_bCoastPlatformErosion = b; - } - void SetPlatformErosionResistance(double d) - { - m_dPlatformErosionResistance = d; - } - void SetBeachSedimentTransport(bool b) - { - m_bBeachSedimentTransport = b; - } - void SetBeachTransportAtEdges(int n) - { - m_nBeachTransportAtEdges = n; - } - void SetBeachErosionEquation(int n) - { - m_nBeachErosionEquation = n; - } - void SetFineMedianSize(double d) - { - m_dFineMedianSize = d; - } - void SetSandMedianSize(double d) - { - m_dSandMedianSize = d; - } - void SetCoarseMedianSize(double d) - { - m_dCoarseMedianSize = d; - } - void SetSedimentDensity(double d) - { - m_dSedimentDensity = d; - } - void SetBeachSedimentPorosity(double d) - { - m_dBeachSedimentPorosity = d; - } - void SetFineErosivity(double d) - { - m_dFineErosivity = d; - } - void SetSandErosivity(double d) - { - m_dSandErosivity = d; - } - void SetCoarseErosivity(double d) - { - m_dCoarseErosivity = d; - } - void SetTransportKLS(double d) - { - m_dTransportKLS = d; - } - void SetKamphuis(double d) - { - m_dKamphuis = d; - } - void SetBermHeight(double d) - { - m_dBermHeight = d; - } - - void SetCliffCollapse(bool b) - { - m_bCliffCollapse = b; - } - void SetCliffErosionResistance(double d) - { - m_dCliffErosionResistance = d; - } - void SetNotchOverhang(double d) - { - m_dNotchOverhang = d; - } - void SetNotchBase(double d) - { - m_dNotchBase = d; - } - void SetCliffDepositionA(double d) - { - m_dCliffDepositionA = d; - } - void SetTalusWidth(double d) - { - m_dTalusWidth = d; - } - void SetMinTalusLength(double d) - { - m_dMinTalusLength = d; - } - void SetMinTalusHeight(double d) - { - m_dMinTalusHeight = d; - } - - void SetFloodInput(bool b) - { - m_bFloodInput = b; - } - void SetFloodFiles(vector v) - { - m_vecFloodFiles = v; - } - void SetFloodCoastline(string const &str) - { - m_strFloodCoastline = str; - } - void SetRunupEquation(string const &str) - { - if (str == "" or str == " ") - { - m_strRunupEquation = 0; - } - else - { - m_strRunupEquation = std::stoi(str); - } - } - void SetFloodLocations(string const &str) - { - m_strFloodLocations = str; - } - void SetFloodInputLocation(string const &str) - { - m_strFloodInputLocation = str; - } - - void SetSedimentInput(bool b) - { - m_bSedimentInput = b; - } - void SetSedimentInputLocation(string const &str) - { - m_strSedimentInputLocation = str; - } - void SetSedimentInputType(string const &str) - { - m_strSedimentInputType = str; - } - void SetSedimentInputDetails(string const &str) - { - m_strSedimentInputDetails = str; - } - - void SetGravitationalAcceleration(double d) - { - m_dGravitationalAcceleration = d; - } - void SetNormalSpacing(double d) - { - m_dNormalSpacing = d; - } - void SetRandomFactor(double d) - { - m_dRandomFactor = d; - } - void SetNormalLength(double d) - { - m_dNormalLength = d; - } - void SetStartDepthRatio(double d) - { - m_dStartDepthRatio = d; - } - void SetSyntheticTransectSpacing(double d) - { - m_dSyntheticTransectSpacing = d; - } - - void SetSaveProfileData(bool b) - { - m_bSaveProfileData = b; - } - void SetProfileNumbers(vector const &vec) - { - m_vecProfileNumbers = vec; - } - void SetProfileTimesteps(vector const &vec) - { - m_vecProfileTimesteps = vec; - } - void SetSaveParallelProfiles(bool b) - { - m_bSaveParallelProfiles = b; - } - void SetOutputErosionPotential(bool b) - { - m_bOutputErosionPotential = b; - } - void SetCurvatureWindow(int n) - { - m_nCurvatureWindow = n; - } - - void SetCliffEdgeSmoothing(int n) - { - m_nCliffEdgeSmoothing = n; - } - void SetCliffEdgeSmoothingWindow(int n) - { - m_nCliffEdgeSmoothingWindow = n; - } - void SetCliffEdgePolynomialOrder(int n) - { - m_nCliffEdgePolynomialOrder = n; - } - void SetCliffSlopeLimit(double d) - { - m_dCliffSlopeLimit = d; - } - - // Getters for all parameters - string GetRunName() const - { - return m_strRunName; - } - int GetLogFileDetail() const - { - return m_nLogFileDetail; - } - bool GetCSVPerTimestepResults() const - { - return m_bCSVPerTimestepResults; - } - string GetStartDateTime() const - { - return m_strStartDateTime; - } - string GetDuration() const - { - return m_strDuration; - } - string GetTimestep() const - { - return m_strTimestep; - } - vector GetSaveTimes() const - { - return m_vecSaveTimes; - } - int GetRandomSeed() const - { - return m_nRandomSeed; - } - bool UseSystemTimeForRandomSeed() const - { - return m_bUseSystemTimeForSeed; - } - - int GetMaxSaveDigits() const - { - return m_nMaxSaveDigits; - } - string GetSaveDigitsMode() const - { - return m_strSaveDigitsMode; - } - vector GetRasterFiles() const; - string GetRasterFormat() const - { - return m_strRasterFormat; - } - bool GetWorldFile() const - { - return m_bWorldFile; - } - bool GetScaleValues() const - { - return m_bScaleValues; - } - vector GetSliceElevations() const - { - return m_vecSliceElevations; - } - vector GetVectorFiles() const; - string GetVectorFormat() const - { - return m_strVectorFormat; - } - vector GetTimeSeriesFiles() const; - - int GetCoastlineSmoothing() const - { - return m_nCoastlineSmoothing; - } - int GetCoastlineSmoothingWindow() const - { - return m_nCoastlineSmoothingWindow; - } - int GetPolynomialOrder() const - { - return m_nPolynomialOrder; - } - string GetOmitGridEdges() const; - int GetProfileSmoothingWindow() const - { - return m_nProfileSmoothingWindow; - } - double GetMaxLocalSlope() const - { - return m_dMaxLocalSlope; - } - double GetMaxBeachElevation() const - { - return m_dMaxBeachElevation; - } - - int GetNumLayers() const - { - return m_nNumLayers; - } - string GetBasementDEMFile() const - { - return m_strBasementDEMFile; - } - vector GetUnconsFineFiles() const - { - return m_vecUnconsFineFiles; - } - vector GetUnconsSandFiles() const - { - return m_vecUnconsSandFiles; - } - vector GetUnconsCoarseFiles() const - { - return m_vecUnconsCoarseFiles; - } - vector GetConsFineFiles() const - { - return m_vecConsFineFiles; - } - vector GetConsSandFiles() const - { - return m_vecConsSandFiles; - } - vector GetConsCoarseFiles() const - { - return m_vecConsCoarseFiles; - } - string GetSuspendedSedFile() const - { - return m_strSuspendedSedFile; - } - string GetLandformFile() const - { - return m_strLandformFile; - } - string GetInterventionClassFile() const - { - return m_strInterventionClassFile; - } - string GetInterventionHeightFile() const - { - return m_strInterventionHeightFile; - } - - int GetWavePropagationModel() const - { - return m_nWavePropagationModel; - } - double GetSeawaterDensity() const - { - return m_dSeawaterDensity; - } - double GetInitialWaterLevel() const - { - return m_dInitialWaterLevel; - } - double GetFinalWaterLevel() const - { - return m_dFinalWaterLevel; - } - bool HasFinalWaterLevel() const - { - return m_bHasFinalWaterLevel; - } - - // Wave data configuration getters (Cases 37-40) - string GetWaveHeightTimeSeries() const - { - return m_strWaveHeightTimeSeries; - } - string GetWaveStationDataFile() const - { - return m_strWaveStationDataFile; - } - double GetDeepWaterWaveHeight() const - { - return m_dDeepWaterWaveHeight; - } - double GetDeepWaterWaveOrientation() const - { - return m_dDeepWaterWaveOrientation; - } - double GetWavePeriod() const - { - return m_dWavePeriod; - } - - string GetTideDataFile() const - { - return m_strTideDataFile; - } - double GetBreakingWaveRatio() const - { - return m_dBreakingWaveRatio; - } - - // Sediment and Erosion parameters - bool GetCoastPlatformErosion() const - { - return m_bCoastPlatformErosion; - } - double GetPlatformErosionResistance() const - { - return m_dPlatformErosionResistance; - } - bool GetBeachSedimentTransport() const - { - return m_bBeachSedimentTransport; - } - int GetBeachTransportAtEdges() const - { - return m_nBeachTransportAtEdges; - } - int GetBeachErosionEquation() const - { - return m_nBeachErosionEquation; - } - double GetFineMedianSize() const - { - return m_dFineMedianSize; - } - double GetSandMedianSize() const - { - return m_dSandMedianSize; - } - double GetCoarseMedianSize() const - { - return m_dCoarseMedianSize; - } - double GetSedimentDensity() const - { - return m_dSedimentDensity; - } - double GetBeachSedimentPorosity() const - { - return m_dBeachSedimentPorosity; - } - double GetFineErosivity() const - { - return m_dFineErosivity; - } - double GetSandErosivity() const - { - return m_dSandErosivity; - } - double GetCoarseErosivity() const - { - return m_dCoarseErosivity; - } - double GetTransportKLS() const - { - return m_dTransportKLS; - } - double GetKamphuis() const - { - return m_dKamphuis; - } - double GetBermHeight() const - { - return m_dBermHeight; - } - - // Cliff parameters - bool GetCliffCollapse() const - { - return m_bCliffCollapse; - } - double GetCliffErosionResistance() const - { - return m_dCliffErosionResistance; - } - double GetNotchOverhang() const - { - return m_dNotchOverhang; - } - double GetNotchBase() const - { - return m_dNotchBase; - } - double GetCliffDepositionA() const - { - return m_dCliffDepositionA; - } - double GetTalusWidth() const - { - return m_dTalusWidth; - } - double GetMinTalusLength() const - { - return m_dMinTalusLength; - } - double GetMinTalusHeight() const - { - return m_dMinTalusHeight; - } - - // Flood parameters - bool GetFloodInput() const - { - return m_bFloodInput; - } - vector GetFloodFiles() const; - - string GetFloodCoastline() const - { - return m_strFloodCoastline; - } - int GetRunupEquation() const - { - return m_strRunupEquation; - } - string GetFloodLocations() const - { - return m_strFloodLocations; - } - string GetFloodInputLocation() const - { - return m_strFloodInputLocation; - } - - // Sediment Input parameters - bool GetSedimentInput() const - { - return m_bSedimentInput; - } - string GetSedimentInputLocation() const - { - return m_strSedimentInputLocation; - } - string GetSedimentInputType() const - { - return m_strSedimentInputType; - } - string GetSedimentInputDetails() const - { - return m_strSedimentInputDetails; - } - - // Physics and Geometry parameters - double GetGravitationalAcceleration() const - { - return m_dGravitationalAcceleration; - } - double GetNormalSpacing() const - { - return m_dNormalSpacing; - } - double GetRandomFactor() const - { - return m_dRandomFactor; - } - double GetNormalLength() const - { - return m_dNormalLength; - } - double GetStartDepthRatio() const - { - return m_dStartDepthRatio; - } - double GetSyntheticTransectSpacing() const - { - return m_dSyntheticTransectSpacing; - } - - // Profile and Output Options - bool GetSaveProfileData() const - { - return m_bSaveProfileData; - } - vector GetProfileNumbers() const - { - return m_vecProfileNumbers; - } - vector GetProfileTimesteps() const - { - return m_vecProfileTimesteps; - } - bool GetSaveParallelProfiles() const - { - return m_bSaveParallelProfiles; - } - bool GetOutputErosionPotential() const - { - return m_bOutputErosionPotential; - } - int GetCurvatureWindow() const - { - return m_nCurvatureWindow; - } - - // Cliff Edge Processing - int GetCliffEdgeSmoothing() const - { - return m_nCliffEdgeSmoothing; - } - int GetCliffEdgeSmoothingWindow() const - { - return m_nCliffEdgeSmoothingWindow; - } - int GetCliffEdgePolynomialOrder() const - { - return m_nCliffEdgePolynomialOrder; - } - double GetCliffSlopeLimit() const - { - return m_dCliffSlopeLimit; - } - // Initialize with default values - void InitializeDefaults(); -}; -#endif //CONFIGURATION_H +/*! + + \file configuration.h + \brief Unified configuration class for CoastalME simulation parameters + \details Provides a single interface for accessing simulation parameters + regardless of input format (.dat or YAML) + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License + +*/ + +/* ============================================================================================================================== + + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + + This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +==============================================================================================================================*/ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include +#include +#include +#include + +using std::string; +using std::vector; + +//! Unified configuration class for CoastalME simulation parameters +class CConfiguration +{ + private: + // Run Information + string m_strRunName; + int m_nLogFileDetail; + bool m_bCSVPerTimestepResults; + + // Simulation timing + string m_strStartDateTime; + string m_strDuration; + string m_strTimestep; + vector m_vecSaveTimes; + int m_nRandomSeed; + bool m_bUseSystemTimeForSeed; + + // GIS Output + int m_nMaxSaveDigits; + string m_strSaveDigitsMode; + vector m_vecRasterFiles; + string m_strRasterFormat; + bool m_bWorldFile; + bool m_bScaleValues; + vector m_vecSliceElevations; + vector m_vecVectorFiles; + string m_strVectorFormat; + vector m_vecTimeSeriesFiles; + + // Grid and Coastline + int m_nCoastlineSmoothing; + int m_nCoastlineSmoothingWindow; + int m_nPolynomialOrder; + string m_strOmitGridEdges; + int m_nProfileSmoothingWindow; + double m_dMaxLocalSlope; + double m_dMaxBeachElevation; + + // Layers and Files + int m_nNumLayers; + string m_strBasementDEMFile; + vector m_vecUnconsFineFiles; + vector m_vecUnconsSandFiles; + vector m_vecUnconsCoarseFiles; + vector m_vecConsFineFiles; + vector m_vecConsSandFiles; + vector m_vecConsCoarseFiles; + string m_strSuspendedSedFile; + string m_strLandformFile; + string m_strInterventionClassFile; + string m_strInterventionHeightFile; + + // Hydrology + int m_nWavePropagationModel; + double m_dSeawaterDensity; + double m_dInitialWaterLevel; + double m_dFinalWaterLevel; + bool m_bHasFinalWaterLevel; + + // Waves + string m_strWaveHeightTimeSeries; + string m_strWaveStationDataFile; + double m_dDeepWaterWaveHeight; + double m_dDeepWaterWaveOrientation; + double m_dWavePeriod; + + // tides + string m_strTideDataFile; + double m_dBreakingWaveRatio; + + // Sediment and Erosion + bool m_bCoastPlatformErosion; + double m_dPlatformErosionResistance; + bool m_bBeachSedimentTransport; + int m_nBeachTransportAtEdges; + int m_nBeachErosionEquation; + double m_dFineMedianSize; + double m_dSandMedianSize; + double m_dCoarseMedianSize; + double m_dSedimentDensity; + double m_dBeachSedimentPorosity; + double m_dFineErosivity; + double m_dSandErosivity; + double m_dCoarseErosivity; + double m_dTransportKLS; + double m_dKamphuis; + double m_dBermHeight; + + // Cliff parameters + bool m_bCliffCollapse; + double m_dCliffErosionResistance; + double m_dNotchOverhang; + double m_dNotchBase; + double m_dCliffDepositionA; + double m_dTalusWidth; + double m_dMinTalusLength; + double m_dMinTalusHeight; + + // Flood parameters + bool m_bFloodInput; + string m_strFloodCoastline; + int m_strRunupEquation; + string m_strFloodLocations; + string m_strFloodInputLocation; + vector m_vecFloodFiles; + + // Sediment input parameters + bool m_bSedimentInput; + string m_strSedimentInputLocation; + string m_strSedimentInputType; + string m_strSedimentInputDetails; + + // Physics and Geometry + double m_dGravitationalAcceleration; + double m_dNormalSpacing; + double m_dRandomFactor; + double m_dNormalLength; + double m_dStartDepthRatio; + double m_dSyntheticTransectSpacing; + + // Profile and Output Options + bool m_bSaveProfileData; + vector m_vecProfileNumbers; + vector m_vecProfileTimesteps; + bool m_bSaveParallelProfiles; + bool m_bOutputErosionPotential; + int m_nCurvatureWindow; + + // Cliff Edge Processing + int m_nCliffEdgeSmoothing; + int m_nCliffEdgeSmoothingWindow; + int m_nCliffEdgePolynomialOrder; + double m_dCliffSlopeLimit; + + public: + CConfiguration(); + ~CConfiguration(); + + // Setters for all parameters + void SetRunName(string const &str) + { + m_strRunName = str; + } + void SetLogFileDetail(int n) + { + m_nLogFileDetail = n; + } + void SetCSVPerTimestepResults(bool b) + { + m_bCSVPerTimestepResults = b; + } + void SetStartDateTime(string const &str) + { + m_strStartDateTime = str; + } + void SetDuration(string const &str) + { + m_strDuration = str; + } + void SetTimestep(string const &str) + { + m_strTimestep = str; + } + void SetSaveTimes(vector const &vec) + { + m_vecSaveTimes = vec; + } + void SetRandomSeed(int n) + { + m_nRandomSeed = n; + m_bUseSystemTimeForSeed = false; + } + void UseSystemTimeForSeed() + { + m_bUseSystemTimeForSeed = false; + } + + void SetMaxSaveDigits(int n) + { + m_nMaxSaveDigits = n; + } + void SetSaveDigitsMode(string const &str) + { + m_strSaveDigitsMode = str; + } + void SetRasterFiles(vector const &vec) + { + m_vecRasterFiles = vec; + } + void SetRasterFormat(string const &str) + { + m_strRasterFormat = str; + } + void SetWorldFile(bool b) + { + m_bWorldFile = b; + } + void SetScaleValues(bool b) + { + m_bScaleValues = b; + } + void SetSliceElevations(vector const &vec) + { + m_vecSliceElevations = vec; + } + void SetVectorFiles(vector const &vec) + { + m_vecVectorFiles = vec; + } + void SetVectorFormat(string const &str) + { + m_strVectorFormat = str; + } + void SetTimeSeriesFiles(vector const &vec) + { + m_vecTimeSeriesFiles = vec; + } + + void SetCoastlineSmoothing(int n) + { + m_nCoastlineSmoothing = n; + } + void SetCoastlineSmoothingWindow(int n) + { + m_nCoastlineSmoothingWindow = n; + } + void SetPolynomialOrder(int n) + { + m_nPolynomialOrder = n; + } + void SetOmitGridEdges(string const &str) + { + m_strOmitGridEdges = str; + } + void SetProfileSmoothingWindow(int n) + { + m_nProfileSmoothingWindow = n; + } + void SetMaxLocalSlope(double d) + { + m_dMaxLocalSlope = d; + } + void SetMaxBeachElevation(double d) + { + m_dMaxBeachElevation = d; + } + void SetNumLayers(int n) + { + m_nNumLayers = n; + } + void SetBasementDEMFile(string const &str) + { + m_strBasementDEMFile = str; + } + void SetUnconsFineFiles(vector const &vec) + { + m_vecUnconsFineFiles = vec; + } + void SetUnconsSandFiles(vector const &vec) + { + m_vecUnconsSandFiles = vec; + } + void SetUnconsCoarseFiles(vector const &vec) + { + m_vecUnconsCoarseFiles = vec; + } + void SetConsFineFiles(vector const &vec) + { + m_vecConsFineFiles = vec; + } + void SetConsSandFiles(vector const &vec) + { + m_vecConsSandFiles = vec; + } + void SetConsCoarseFiles(vector const &vec) + { + m_vecConsCoarseFiles = vec; + } + void SetSuspendedSedFile(string const &str) + { + m_strSuspendedSedFile = str; + } + void SetLandformFile(string const &str) + { + m_strLandformFile = str; + } + void SetInterventionClassFile(string const &str) + { + m_strInterventionClassFile = str; + } + void SetInterventionHeightFile(string const &str) + { + m_strInterventionHeightFile = str; + } + + void SetWavePropagationModel(int n) + { + m_nWavePropagationModel = n; + } + void SetSeawaterDensity(double d) + { + m_dSeawaterDensity = d; + } + void SetInitialWaterLevel(double d) + { + m_dInitialWaterLevel = d; + } + void SetFinalWaterLevel(double d) + { + m_dFinalWaterLevel = d; + m_bHasFinalWaterLevel = true; + } + + // Wave height Data + void SetWaveHeightTimeSeries(string const &str) + { + m_strWaveHeightTimeSeries = str; + } + void SetWaveStationDataFile(string const &str) + { + m_strWaveStationDataFile = str; + } + void SetDeepWaterWaveHeight(double d) + { + m_dDeepWaterWaveHeight = d; + } + void SetDeepWaterWaveOrientation(double d) + { + m_dDeepWaterWaveOrientation = d; + } + void SetWavePeriod(double d) + { + m_dWavePeriod = d; + } + + void SetTideDataFile(string const &str) + { + m_strTideDataFile = str; + } + void SetBreakingWaveRatio(double d) + { + m_dBreakingWaveRatio = d; + } + + // Additional setters for comprehensive YAML support + void SetCoastPlatformErosion(bool b) + { + m_bCoastPlatformErosion = b; + } + void SetPlatformErosionResistance(double d) + { + m_dPlatformErosionResistance = d; + } + void SetBeachSedimentTransport(bool b) + { + m_bBeachSedimentTransport = b; + } + void SetBeachTransportAtEdges(int n) + { + m_nBeachTransportAtEdges = n; + } + void SetBeachErosionEquation(int n) + { + m_nBeachErosionEquation = n; + } + void SetFineMedianSize(double d) + { + m_dFineMedianSize = d; + } + void SetSandMedianSize(double d) + { + m_dSandMedianSize = d; + } + void SetCoarseMedianSize(double d) + { + m_dCoarseMedianSize = d; + } + void SetSedimentDensity(double d) + { + m_dSedimentDensity = d; + } + void SetBeachSedimentPorosity(double d) + { + m_dBeachSedimentPorosity = d; + } + void SetFineErosivity(double d) + { + m_dFineErosivity = d; + } + void SetSandErosivity(double d) + { + m_dSandErosivity = d; + } + void SetCoarseErosivity(double d) + { + m_dCoarseErosivity = d; + } + void SetTransportKLS(double d) + { + m_dTransportKLS = d; + } + void SetKamphuis(double d) + { + m_dKamphuis = d; + } + void SetBermHeight(double d) + { + m_dBermHeight = d; + } + + void SetCliffCollapse(bool b) + { + m_bCliffCollapse = b; + } + void SetCliffErosionResistance(double d) + { + m_dCliffErosionResistance = d; + } + void SetNotchOverhang(double d) + { + m_dNotchOverhang = d; + } + void SetNotchBase(double d) + { + m_dNotchBase = d; + } + void SetCliffDepositionA(double d) + { + m_dCliffDepositionA = d; + } + void SetTalusWidth(double d) + { + m_dTalusWidth = d; + } + void SetMinTalusLength(double d) + { + m_dMinTalusLength = d; + } + void SetMinTalusHeight(double d) + { + m_dMinTalusHeight = d; + } + + void SetFloodInput(bool b) + { + m_bFloodInput = b; + } + void SetFloodFiles(vector v) + { + m_vecFloodFiles = v; + } + void SetFloodCoastline(string const &str) + { + m_strFloodCoastline = str; + } + void SetRunupEquation(string const &str) + { + if (str == "" or str == " ") + { + m_strRunupEquation = 0; + } + else + { + m_strRunupEquation = std::stoi(str); + } + } + void SetFloodLocations(string const &str) + { + m_strFloodLocations = str; + } + void SetFloodInputLocation(string const &str) + { + m_strFloodInputLocation = str; + } + + void SetSedimentInput(bool b) + { + m_bSedimentInput = b; + } + void SetSedimentInputLocation(string const &str) + { + m_strSedimentInputLocation = str; + } + void SetSedimentInputType(string const &str) + { + m_strSedimentInputType = str; + } + void SetSedimentInputDetails(string const &str) + { + m_strSedimentInputDetails = str; + } + + void SetGravitationalAcceleration(double d) + { + m_dGravitationalAcceleration = d; + } + void SetNormalSpacing(double d) + { + m_dNormalSpacing = d; + } + void SetRandomFactor(double d) + { + m_dRandomFactor = d; + } + void SetNormalLength(double d) + { + m_dNormalLength = d; + } + void SetStartDepthRatio(double d) + { + m_dStartDepthRatio = d; + } + void SetSyntheticTransectSpacing(double d) + { + m_dSyntheticTransectSpacing = d; + } + + void SetSaveProfileData(bool b) + { + m_bSaveProfileData = b; + } + void SetProfileNumbers(vector const &vec) + { + m_vecProfileNumbers = vec; + } + void SetProfileTimesteps(vector const &vec) + { + m_vecProfileTimesteps = vec; + } + void SetSaveParallelProfiles(bool b) + { + m_bSaveParallelProfiles = b; + } + void SetOutputErosionPotential(bool b) + { + m_bOutputErosionPotential = b; + } + void SetCurvatureWindow(int n) + { + m_nCurvatureWindow = n; + } + + void SetCliffEdgeSmoothing(int n) + { + m_nCliffEdgeSmoothing = n; + } + void SetCliffEdgeSmoothingWindow(int n) + { + m_nCliffEdgeSmoothingWindow = n; + } + void SetCliffEdgePolynomialOrder(int n) + { + m_nCliffEdgePolynomialOrder = n; + } + void SetCliffSlopeLimit(double d) + { + m_dCliffSlopeLimit = d; + } + + // Getters for all parameters + string GetRunName() const + { + return m_strRunName; + } + int GetLogFileDetail() const + { + return m_nLogFileDetail; + } + bool GetCSVPerTimestepResults() const + { + return m_bCSVPerTimestepResults; + } + string GetStartDateTime() const + { + return m_strStartDateTime; + } + string GetDuration() const + { + return m_strDuration; + } + string GetTimestep() const + { + return m_strTimestep; + } + vector GetSaveTimes() const + { + return m_vecSaveTimes; + } + int GetRandomSeed() const + { + return m_nRandomSeed; + } + bool UseSystemTimeForRandomSeed() const + { + return m_bUseSystemTimeForSeed; + } + + int GetMaxSaveDigits() const + { + return m_nMaxSaveDigits; + } + string GetSaveDigitsMode() const + { + return m_strSaveDigitsMode; + } + vector GetRasterFiles() const; + string GetRasterFormat() const + { + return m_strRasterFormat; + } + bool GetWorldFile() const + { + return m_bWorldFile; + } + bool GetScaleValues() const + { + return m_bScaleValues; + } + vector GetSliceElevations() const + { + return m_vecSliceElevations; + } + vector GetVectorFiles() const; + string GetVectorFormat() const + { + return m_strVectorFormat; + } + vector GetTimeSeriesFiles() const; + + int GetCoastlineSmoothing() const + { + return m_nCoastlineSmoothing; + } + int GetCoastlineSmoothingWindow() const + { + return m_nCoastlineSmoothingWindow; + } + int GetPolynomialOrder() const + { + return m_nPolynomialOrder; + } + string GetOmitGridEdges() const; + int GetProfileSmoothingWindow() const + { + return m_nProfileSmoothingWindow; + } + double GetMaxLocalSlope() const + { + return m_dMaxLocalSlope; + } + double GetMaxBeachElevation() const + { + return m_dMaxBeachElevation; + } + + int GetNumLayers() const + { + return m_nNumLayers; + } + string GetBasementDEMFile() const + { + return m_strBasementDEMFile; + } + vector GetUnconsFineFiles() const + { + return m_vecUnconsFineFiles; + } + vector GetUnconsSandFiles() const + { + return m_vecUnconsSandFiles; + } + vector GetUnconsCoarseFiles() const + { + return m_vecUnconsCoarseFiles; + } + vector GetConsFineFiles() const + { + return m_vecConsFineFiles; + } + vector GetConsSandFiles() const + { + return m_vecConsSandFiles; + } + vector GetConsCoarseFiles() const + { + return m_vecConsCoarseFiles; + } + string GetSuspendedSedFile() const + { + return m_strSuspendedSedFile; + } + string GetLandformFile() const + { + return m_strLandformFile; + } + string GetInterventionClassFile() const + { + return m_strInterventionClassFile; + } + string GetInterventionHeightFile() const + { + return m_strInterventionHeightFile; + } + + int GetWavePropagationModel() const + { + return m_nWavePropagationModel; + } + double GetSeawaterDensity() const + { + return m_dSeawaterDensity; + } + double GetInitialWaterLevel() const + { + return m_dInitialWaterLevel; + } + double GetFinalWaterLevel() const + { + return m_dFinalWaterLevel; + } + bool HasFinalWaterLevel() const + { + return m_bHasFinalWaterLevel; + } + + // Wave data configuration getters (Cases 37-40) + string GetWaveHeightTimeSeries() const + { + return m_strWaveHeightTimeSeries; + } + string GetWaveStationDataFile() const + { + return m_strWaveStationDataFile; + } + double GetDeepWaterWaveHeight() const + { + return m_dDeepWaterWaveHeight; + } + double GetDeepWaterWaveOrientation() const + { + return m_dDeepWaterWaveOrientation; + } + double GetWavePeriod() const + { + return m_dWavePeriod; + } + + string GetTideDataFile() const + { + return m_strTideDataFile; + } + double GetBreakingWaveRatio() const + { + return m_dBreakingWaveRatio; + } + + // Sediment and Erosion parameters + bool GetCoastPlatformErosion() const + { + return m_bCoastPlatformErosion; + } + double GetPlatformErosionResistance() const + { + return m_dPlatformErosionResistance; + } + bool GetBeachSedimentTransport() const + { + return m_bBeachSedimentTransport; + } + int GetBeachTransportAtEdges() const + { + return m_nBeachTransportAtEdges; + } + int GetBeachErosionEquation() const + { + return m_nBeachErosionEquation; + } + double GetFineMedianSize() const + { + return m_dFineMedianSize; + } + double GetSandMedianSize() const + { + return m_dSandMedianSize; + } + double GetCoarseMedianSize() const + { + return m_dCoarseMedianSize; + } + double GetSedimentDensity() const + { + return m_dSedimentDensity; + } + double GetBeachSedimentPorosity() const + { + return m_dBeachSedimentPorosity; + } + double GetFineErosivity() const + { + return m_dFineErosivity; + } + double GetSandErosivity() const + { + return m_dSandErosivity; + } + double GetCoarseErosivity() const + { + return m_dCoarseErosivity; + } + double GetTransportKLS() const + { + return m_dTransportKLS; + } + double GetKamphuis() const + { + return m_dKamphuis; + } + double GetBermHeight() const + { + return m_dBermHeight; + } + + // Cliff parameters + bool GetCliffCollapse() const + { + return m_bCliffCollapse; + } + double GetCliffErosionResistance() const + { + return m_dCliffErosionResistance; + } + double GetNotchOverhang() const + { + return m_dNotchOverhang; + } + double GetNotchBase() const + { + return m_dNotchBase; + } + double GetCliffDepositionA() const + { + return m_dCliffDepositionA; + } + double GetTalusWidth() const + { + return m_dTalusWidth; + } + double GetMinTalusLength() const + { + return m_dMinTalusLength; + } + double GetMinTalusHeight() const + { + return m_dMinTalusHeight; + } + + // Flood parameters + bool GetFloodInput() const + { + return m_bFloodInput; + } + vector GetFloodFiles() const; + + string GetFloodCoastline() const + { + return m_strFloodCoastline; + } + int GetRunupEquation() const + { + return m_strRunupEquation; + } + string GetFloodLocations() const + { + return m_strFloodLocations; + } + string GetFloodInputLocation() const + { + return m_strFloodInputLocation; + } + + // Sediment Input parameters + bool GetSedimentInput() const + { + return m_bSedimentInput; + } + string GetSedimentInputLocation() const + { + return m_strSedimentInputLocation; + } + string GetSedimentInputType() const + { + return m_strSedimentInputType; + } + string GetSedimentInputDetails() const + { + return m_strSedimentInputDetails; + } + + // Physics and Geometry parameters + double GetGravitationalAcceleration() const + { + return m_dGravitationalAcceleration; + } + double GetNormalSpacing() const + { + return m_dNormalSpacing; + } + double GetRandomFactor() const + { + return m_dRandomFactor; + } + double GetNormalLength() const + { + return m_dNormalLength; + } + double GetStartDepthRatio() const + { + return m_dStartDepthRatio; + } + double GetSyntheticTransectSpacing() const + { + return m_dSyntheticTransectSpacing; + } + + // Profile and Output Options + bool GetSaveProfileData() const + { + return m_bSaveProfileData; + } + vector GetProfileNumbers() const + { + return m_vecProfileNumbers; + } + vector GetProfileTimesteps() const + { + return m_vecProfileTimesteps; + } + bool GetSaveParallelProfiles() const + { + return m_bSaveParallelProfiles; + } + bool GetOutputErosionPotential() const + { + return m_bOutputErosionPotential; + } + int GetCurvatureWindow() const + { + return m_nCurvatureWindow; + } + + // Cliff Edge Processing + int GetCliffEdgeSmoothing() const + { + return m_nCliffEdgeSmoothing; + } + int GetCliffEdgeSmoothingWindow() const + { + return m_nCliffEdgeSmoothingWindow; + } + int GetCliffEdgePolynomialOrder() const + { + return m_nCliffEdgePolynomialOrder; + } + double GetCliffSlopeLimit() const + { + return m_dCliffSlopeLimit; + } + // Initialize with default values + void InitializeDefaults(); +}; +#endif //CONFIGURATION_H diff --git a/src/create_profiles.cpp b/src/create_profiles.cpp index 018d7ae04..2a9da1ac9 100644 --- a/src/create_profiles.cpp +++ b/src/create_profiles.cpp @@ -1,2285 +1,2285 @@ -/*! - \file create_profiles.cpp - \brief Creates profiles which are approximately normal to the coastline, these will become inter-polygon boundaries - \details TODO 001 A more detailed description of these routines. - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* ============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -==============================================================================================================================*/ -#include - -#include -#include - -#include -using std::cerr; -using std::endl; -using std::ios; - -#include -using std::find; -using std::sort; - -#include -using std::make_pair; -using std::pair; - -#include -using std::normal_distribution; - -#include "cme.h" -#include "simulation.h" -#include "coast.h" -#include "2d_point.h" -#include "2di_point.h" - -namespace -{ -//=============================================================================================================================== -//! Function used to sort coastline curvature values when locating start points of normal profiles -//=============================================================================================================================== -bool bCurvaturePairCompareDescending(const pair& prLeft, const pair& prRight) -{ - // Sort in descending order (i.e. most concave first) - return prLeft.second > prRight.second; -} -} // namespace - -//=============================================================================================================================== -//! Create coastline-normal profiles for all coastlines. The first profiles are created 'around' the most concave bits of coast. Also create 'special' profiles at the start and end of the coast, and put these onto the raster grid -//=============================================================================================================================== -int CSimulation::nCreateAllProfiles(void) -{ - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << endl << m_ulIter << ": Creating profiles" << endl; - - for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++) - { - int nProfile = 0; - int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); - - // Create a bool vector to mark coast points which have been searched - vector bVCoastPointDone(nCoastSize, false); - - // Now create a vector of pairs: the first value of the pair is the coastline point, the second is the coastline's curvature at that point - vector> prVCurvature; - - for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) - { - double dCurvature; - - int const nCat = m_VCoast[nCoast].pGetCoastLandform(nCoastPoint)->nGetLandFormCategory(); - if ((nCat != LF_INTERVENTION_STRUCT) && (nCat != LF_INTERVENTION_NON_STRUCT)) - { - // Not an intervention coast point, so store the smoothed curvature - dCurvature = m_VCoast[nCoast].dGetSmoothCurvature(nCoastPoint); - } - else - { - // This is an intervention coast point, which is likely to have some sharp angles. So store the detailed curvature - dCurvature = m_VCoast[nCoast].dGetDetailedCurvature(nCoastPoint); - } - - prVCurvature.push_back(make_pair(nCoastPoint, dCurvature)); - } - - // Sort this pair vector in descending order, so that the most convex curvature points are first - sort(prVCurvature.begin(), prVCurvature.end(), bCurvaturePairCompareDescending); - - // // DEBUG CODE ======================================================================================================================= - // for (int n = 0; n < prVCurvature.size(); n++) - // { - // LogStream << prVCurvature[n].first << "\t" << prVCurvature[n].second << endl; - // } - // LogStream << endl << endl; - // // DEBUG CODE ======================================================================================================================= - - // And mark points at and near the start and end of the coastline so that they don't get searched (will be creating 'special' start- and end-of-coast profiles at these end points later) - for (int n = 0; n < m_nCoastNormalSpacing; n++) - { - if (n < nCoastSize) - bVCoastPointDone[n] = true; - - int const m = nCoastSize - n - 1; - - if (m >= 0) - bVCoastPointDone[m] = true; - } - - // Now locate the start points for all coastline-normal profiles (except the grid-edge ones), at points of maximum convexity. Then create the profiles - LocateAndCreateProfiles(nCoast, nProfile, &bVCoastPointDone, &prVCurvature); - - // Did we fail to create any normal profiles? If so, quit - if (nProfile < 0) - { - string strErr = ERR + "timestep " + strDblToStr(m_ulIter) + ": could not create profiles for coastline " + strDblToStr(nCoast); - - if (m_ulIter == 1) - strErr += ". Check the SWL"; - - strErr += "\n"; - - cerr << strErr; - LogStream << strErr; - - return RTN_ERR_NO_PROFILES_1; - } - - // Locate and create a 'special' profile at the grid edge, first at the beginning of the coastline. Then put this onto the raster grid - int nRet = nLocateAndCreateGridEdgeProfile(true, nCoast, nProfile); - - if (nRet != RTN_OK) - return nRet; - - // Locate a second 'special' profile at the grid edge, this time at end of the coastline. Then put this onto the raster grid - nRet = nLocateAndCreateGridEdgeProfile(false, nCoast, ++nProfile); - - if (nRet != RTN_OK) - return nRet; - - // Insert pointers to profiles at coastline points in the profile-all-coastpoint index - m_VCoast[nCoast].InsertProfilesInProfileCoastPointIndex(); - - // // DEBUG CODE =================================================================================================== - // LogStream << endl << "===========================================================================================" << endl; - // LogStream << "PROFILES BEFORE ADDING BEFORE- AND AFTER-PROFILE NUMBERS" << endl; - // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles(); - // for (int nn = 0; nn < nNumProfiles; nn++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn); - // - // LogStream << nn << " nCoastID = " << pProfile->nGetProfileID() << " nGlobalID = " << pProfile->nGetProfileID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; - // } - // LogStream << "===================================================================================================" << endl << endl; - // // DEBUG CODE =================================================================================================== - - CGeomProfile* pLastProfile; - CGeomProfile* pThisProfile; - - // Go along the coastline and give each profile the number of the adjacent up-coast profile and the adjacent down-coast profile - for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) - { - if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint)) - { - pThisProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint); - // nThisProfile = pThisProfile->nGetProfileID(); - - if (nCoastPoint == 0) - { - pThisProfile->SetUpCoastAdjacentProfile(NULL); - - // LogStream << "nCoastPoint = " << nCoastPoint << " ThisProfile = " << nThisProfile << " ThisProfile UpCoast = " << pThisProfile->pGetUpCoastAdjacentProfile() << " ThisProfile DownCoast = " << pThisProfile->pGetDownCoastAdjacentProfile() << endl; - - pLastProfile = pThisProfile; - // nLastProfile = nThisProfile; - continue; - } - - pLastProfile->SetDownCoastAdjacentProfile(pThisProfile); - pThisProfile->SetUpCoastAdjacentProfile(pLastProfile); - - if (nCoastPoint == nCoastSize - 1) - pThisProfile->SetDownCoastAdjacentProfile(NULL); - - pLastProfile = pThisProfile; - } - } - - // And create an index to this coast's profiles in along-coastline sequence - m_VCoast[nCoast].CreateProfileDownCoastIndex(); - - // // DEBUG CODE ======================================================================================================================= - // for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); - // CGeomProfile* pUpCoastProfile = pProfile->pGetUpCoastAdjacentProfile(); - // CGeomProfile* pDownCoastProfile = pProfile->pGetDownCoastAdjacentProfile(); - // int nUpCoastProfile = INT_NODATA; - // int nDownCoastProfile = INT_NODATA; - // if (pUpCoastProfile != 0) - // nUpCoastProfile = pUpCoastProfile->nGetProfileID(); - // if (pDownCoastProfile != 0) - // nDownCoastProfile = pDownCoastProfile->nGetProfileID(); - // LogStream << "nCoastID = " << pProfile->nGetProfileID() << "\t up-coast profile = " << nUpCoastProfile << "\t down-coast profile = " << nDownCoastProfile << endl; - // } - // LogStream << endl; - // // DEBUG CODE ======================================================================================================================= - - // // DEBUG CODE ======================================================================================================================= - // int nProf = 0; - // for (int n = 0; n < nCoastSize; n++) - // { - // // LogStream << n << "\t"; - // - // // LogStream << m_VCoast[nCoast].dGetDetailedCurvature(n) << "\t"; - // // - // // LogStream << m_VCoast[nCoast].dGetSmoothCurvature(n) << "\t"; - // // - // // if (m_VCoast[nCoast].pGetCoastLandform(n)->nGetLandFormCategory() == LF_INTERVENTION) - // // LogStream << "I\t"; - // - // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(n)) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(n); - // - // LogStream << "profile " << pProfile->nGetProfileID() << " at coast point " << n << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; - // - // nProf++; - // } - // } - // LogStream << endl; - // LogStream << "nProf = " << nProf << endl; - // // DEBUG CODE ======================================================================================================================= - - // // DEBUG CODE ======================================================================================================================= - // LogStream << "=====================" << endl; - // for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); - // int nStartPoint = pProfile->nGetCoastPoint(); - // - // LogStream << n << "\t nCoastID = " << pProfile->nGetProfileID() << "\tnStartPoint = " << nStartPoint << endl; - // } - // LogStream << endl; - // LogStream << "=====================" << endl; - // // DEBUG CODE ======================================================================================================================= - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! For a single coastline, locate the start points for all coastline-normal profiles (except the grid-edge profiles). Then create the profiles -//=============================================================================================================================== -void CSimulation::LocateAndCreateProfiles(int const nCoast, int& nProfile, vector* pbVCoastPointDone, vector> const* prVCurvature) -{ - int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); - - // Work along the vector of curvature pairs starting at the convex end - for (int n = nCoastSize - 1; n >= 0; - n--) - { - // Have we searched all the coastline points? - int nStillToSearch = 0; - - for (int m = 0; m < nCoastSize; m++) - if (! pbVCoastPointDone->at(m)) - nStillToSearch++; - - if (nStillToSearch == 0) - // OK we are done here - return; - - // This convex point on the coastline is a potential location for a normal - int const nNormalPoint = prVCurvature->at(n).first; - - // Ignore each end of the coastline - if ((nNormalPoint == 0) || (nNormalPoint == nCoastSize - 1)) - continue; - - // TODO 089 When choosing locations for profiles, do coast first then interventions - - if (! pbVCoastPointDone->at(nNormalPoint)) - { - // We have not already searched this coast point. Is it an intervention coast point? - bool bIntervention = false; - - int const nCat = m_VCoast[nCoast].pGetCoastLandform(nNormalPoint)->nGetLandFormCategory(); - if ((nCat == LF_INTERVENTION_STRUCT) || (nCat == LF_INTERVENTION_NON_STRUCT)) - { - // It is an intervention - bIntervention = true; - } - - CGeom2DIPoint const PtiThis = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nNormalPoint); - - // Create a profile here - int const nRet = nCreateProfile(nCoast, nCoastSize, nNormalPoint, nProfile, bIntervention, &PtiThis); - - // // DEBUG CODE ================= - // LogStream << "After nCreateProfile() ===========" << endl; - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); - // LogStream << pProfile->nGetProfileID() << "\t"; - // - // int nPointsInProfile = pProfile->nGetProfileSize(); - // - // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++) - // { - // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint); - // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}"; - // } - // LogStream << endl << "===========" << endl; - // // DEBUG CODE ================= - - // Mark this coast point as searched - pbVCoastPointDone->at(nNormalPoint) = true; - - if (nRet != RTN_OK) - { - // This potential profile is no good (has hit coast, or hit dry land, etc.) so forget about it - // LogStream << "Profile is no good" << endl; - continue; - } - - // // DEBUG CODE =================================================================================================== - // LogStream << endl << "===========================================================================================" << endl; - // LogStream << "PROFILES JUST AFTER CREATION" << endl; - // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles(); - // for (int nn = 0; nn < nNumProfiles; nn++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn); - // - // LogStream << nn << " nCoastID = " << pProfile->nGetProfileID() << " nGlobalID = " << pProfile->nGetProfileID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; - // } - // LogStream << "===================================================================================================" << endl << endl; - // // DEBUG CODE =================================================================================================== - // - // // DEBUG CODE =================================================================================================== - // LogStream << "++++++++++++++++++++++" << endl; - // LogStream << endl << "Just created profile " << nProfile << endl; - // int nProf = 0; - // for (int nnn = 0; nnn < nCoastSize; nnn++) - // { - // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nnn)) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nnn); - // - // LogStream << "profile " << pProfile->nGetProfileID() << " at coast point " << nnn << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; - // - // nProf++; - // } - // } - // LogStream << endl; - // LogStream << "nProf = " << nProf << endl; - // LogStream << "++++++++++++++++++++++" << endl; - // // DEBUG CODE =================================================================================================== - - // CGeom2DPoint PtThis = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nNormalPoint); - // if (m_nLogFileDetail >= LOG_FILE_ALL) - // LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " created at coast point " << nNormalPoint << " [" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] = {" << PtThis.dGetX() << ", " << PtThis.dGetY() << "} (smoothed curvature = " << m_VCoast[nCoast].dGetSmoothCurvature(nNormalPoint) << ", detailed curvature = " << m_VCoast[nCoast].dGetDetailedCurvature(nNormalPoint) << ")" << endl; - - // // DEBUG CODE ================================================================================= - // if (m_pRasterGrid->m_Cell[PtiThis.nGetX()][PtiThis.nGetY()].bIsCoastline()) - // LogStream << m_ulIter << ": cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS coastline, coast number = " << m_pRasterGrid->m_Cell[PtiThis.nGetX()][PtiThis.nGetY()].nGetCoastline() << endl; - // else - // LogStream << m_ulIter << ": ******* cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS NOT coastline" << endl; - // // DEBUG CODE ================================================================================= - - // This profile is fine - nProfile++; - - // We need to mark points on either side of this profile so that we don't get profiles which are too close together. However best-placed profiles on narrow intervention structures may need to be quite closes - double dNumToMark = m_nCoastNormalSpacing; - - if (bIntervention) - dNumToMark = m_nCoastNormalInterventionSpacing; - - // If we have a random factor for profile spacing, then modify the profile spacing - if (m_dCoastNormalRandSpacingFactor > 0) - { - // Draw a sample from the unit normal distribution using random number generator 0 - double const dRand = m_dGetFromUnitNormalDist(m_Rand[0]); - - double const dTmp = dRand * m_dCoastNormalRandSpacingFactor * dNumToMark; - dNumToMark += dTmp; - - // Make sure number to mark is not too small or too big TODO 011 - if (bIntervention) - { - dNumToMark = tMin(dNumToMark, m_nCoastNormalInterventionSpacing * 0.75); - dNumToMark = tMax(dNumToMark, m_nCoastNormalInterventionSpacing * 1.25); - } - - else - { - dNumToMark = tMin(dNumToMark, m_nCoastNormalSpacing * 0.75); - dNumToMark = tMax(dNumToMark, m_nCoastNormalSpacing * 1.25); - } - - // TODO 014 Assume that the above is the profile spacing on straight bits of coast. Try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity. Could use a Michaelis-Menten S-curve relationship for this i.e. - // double fReN = pow(NowCell[nX][nY].dGetReynolds(m_dNu), m_dDepN); - // double fC1 = m_dC1Laminar - ((m_dC1Diff * fReN) / (fReN + m_dReMidN)); - } - - // Mark points on either side of the profile - for (int m = 1; m < dNumToMark; m++) - { - int nTmpPoint = nNormalPoint + m; - - if (nTmpPoint < nCoastSize) - pbVCoastPointDone->at(nTmpPoint) = true; - - nTmpPoint = nNormalPoint - m; - - if (nTmpPoint >= 0) - pbVCoastPointDone->at(nTmpPoint) = true; - } - } - } -} - -//=============================================================================================================================== -//! Creates a single coastline-normal profile (which may be an intervention profile) -//=============================================================================================================================== -int CSimulation::nCreateProfile(int const nCoast, int const nCoastSize, int const nProfileStartPoint, int const nProfile, bool const bIntervention, CGeom2DIPoint const* pPtiStart) -{ - // OK, we have flagged the start point of this new coastline-normal profile, so create it. Make the start of the profile the centroid of the actual cell that is marked as coast (not the cell under the smoothed vector coast, they may well be different) - CGeom2DPoint PtStart; // In external CRS - PtStart.SetX(dGridCentroidXToExtCRSX(pPtiStart->nGetX())); - PtStart.SetY(dGridCentroidYToExtCRSY(pPtiStart->nGetY())); - - CGeom2DPoint PtEnd; // In external CRS - CGeom2DIPoint PtiEnd; // In grid CRS - int const nRet = nGetCoastNormalEndPoint(nCoast, nProfileStartPoint, nCoastSize, &PtStart, m_dCoastNormalLength, &PtEnd, &PtiEnd, bIntervention); - if (nRet == RTN_ERR_NO_SOLUTION_FOR_ENDPOINT) - { - // Could not solve end-point equation, so forget about this profile - return nRet; - } - - int const nXEnd = PtiEnd.nGetX(); - int const nYEnd = PtiEnd.nGetY(); - - // Safety check: is the end point in the contiguous sea? - if (! m_pRasterGrid->m_Cell[nXEnd][nYEnd].bIsInContiguousSea()) - { - // if (m_nLogFileDetail >= LOG_FILE_ALL) - // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " has inland end point at [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl; - - return RTN_ERR_PROFILE_ENDPOINT_IS_INLAND; - } - - // Safety check: is the water depth at the end point less than the depth of closure? - if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure) - { - // if (m_nLogFileDetail >= LOG_FILE_ALL) - // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl; - - return RTN_ERR_PROFILE_END_INSUFFICIENT_DEPTH; - } - - // No problems, so create the new profile - CGeomProfile* pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, bIntervention); - - // And create the profile's coastline-normal vector. Only two points (start and end points, both external CRS) are stored - vector VNormal; - VNormal.push_back(PtStart); - VNormal.push_back(PtEnd); - - // Set the start and end points (external CRS) of the profile - pProfile->SetPointsInProfile(&VNormal); - - // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment - pProfile->AppendLineSegment(); - pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0)); - - // Save the profile, note that several fields in the profile are still blank - m_VCoast[nCoast].AppendProfile(pProfile); - - // // DEBUG CODE ================= - // LogStream << "in nCreateProfile() ===========" << endl; - // // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); - // LogStream << pProfile->nGetProfileID() << "\t"; - // - // int nPointsInProfile = pProfile->nGetProfileSize(); - // - // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++) - // { - // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint); - // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}"; - // } - // LogStream << endl << "===========" << endl; - // // DEBUG CODE ================= - - // assert(pProfile->nGetProfileSize() > 0); - - LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " created at coast point " << nProfileStartPoint << " from [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] = {" << PtStart.dGetX() << ", " << PtStart.dGetY() << "} to [" << PtiEnd.nGetX() << "][" << PtiEnd.nGetY() << "] = {" << PtEnd.dGetX() << ", " << PtEnd.dGetY() << "}" << (pProfile->bIsIntervention() ? ", from intervention" : "") << endl; - - return RTN_OK; -} - -//=============================================================================================================================== -//! Creates a 'special' profile at each end of a coastline, at the edge of the raster grid. This profile is not necessarily normal to the coastline since it goes along the grid's edge -//=============================================================================================================================== -int CSimulation::nLocateAndCreateGridEdgeProfile(bool const bCoastStart, int const nCoast, int& nProfile) -{ - int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); - int const nHandedness = m_VCoast[nCoast].nGetSeaHandedness(); - int const nProfileLen = nRound(m_dCoastNormalLength / m_dCellSide); // Profile length in grid CRS - int nProfileStartEdge; - - CGeom2DIPoint PtiProfileStart; // In grid CRS - vector VPtiNormalPoints; // In grid CRS - - if (bCoastStart) - { - // At start of coast - PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0); // Grid CRS - nProfileStartEdge = m_VCoast[nCoast].nGetStartEdge(); - } - else - { - // At end of coast - PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1); // Grid CRS - nProfileStartEdge = m_VCoast[nCoast].nGetEndEdge(); - } - - VPtiNormalPoints.push_back(PtiProfileStart); - - // Find the start cell in the list of edge cells - auto it = find(m_VEdgeCell.begin(), m_VEdgeCell.end(), PtiProfileStart); - - if (it == m_VEdgeCell.end()) - { - // Not found. This can happen because of rounding problems, i.e. the cell which was stored as the first cell of the raster coastline - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter << ": " << ERR << " when constructing start-of-coast profile, [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} not found in list of edge cells" << endl; - - return RTN_ERR_COAST_CANT_FIND_EDGE_CELL; - } - - // Found - int nPos = static_cast(it - m_VEdgeCell.begin()); - - // Now construct the edge profile, searching for edge cells - for (int n = 0; n < nProfileLen; n++) - { - if (bCoastStart) - { - // At start of coast - if (nHandedness == LEFT_HANDED) - { - // The list of edge cells is in clockwise sequence, go in this direction - nPos++; - - if (nPos >= static_cast(m_VEdgeCell.size())) - { - // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this - break; - } - } - else // Right-handed - { - // The list of edge cells is in clockwise sequence, go in the opposite direction - nPos--; - - if (nPos < 0) - { - // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this - break; - } - } - } - else - { - // At end of coast - if (nHandedness == LEFT_HANDED) - { - // The list of edge cells is in clockwise sequence, go in the opposite direction - nPos--; - - if (nPos < 0) - { - // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this - break; - } - } - else // Right-handed - { - // The list of edge cells is in clockwise sequence, go in this direction - nPos++; - - if (nPos >= static_cast(m_VEdgeCell.size())) - { - // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this - break; - } - } - } - - if (m_VEdgeCellEdge[nPos] != nProfileStartEdge) - { - // We've reached the end of a grid side before the profile is long enough. OK, we can live with this - break; - } - - // All OK, so append this grid-edge cell, making sure that there is no gap between this and the previously-appended cell (if there is, will get problems with cell-by-cell fill) - AppendEnsureNoGap(&VPtiNormalPoints, &m_VEdgeCell[nPos]); - } - - int nProfileStartPoint; - CGeomProfile* pProfile; - CGeom2DIPoint const PtiDummy(INT_NODATA, INT_NODATA); - - if (bCoastStart) - { - nProfileStartPoint = 0; - - // Create the new start-of-coast profile - pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, false); - - // Mark this as a start-of-coast profile - pProfile->SetStartOfCoast(true); - } - else - { - nProfileStartPoint = nCoastSize - 1; - - // Create the new end-of-coast profile - pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, false); - - // Mark this as an end-of-coast profile - pProfile->SetEndOfCoast(true); - } - - // Create the list of cells 'under' this grid-edge profile. Note that more than two cells are stored - for (unsigned int n = 0; n < VPtiNormalPoints.size(); n++) - { - int const nX = VPtiNormalPoints[n].nGetX(); - int const nY = VPtiNormalPoints[n].nGetY(); - - // Mark each cell in the raster grid - m_pRasterGrid->m_Cell[nX][nY].SetCoastAndProfileID(nCoast, nProfile); - - // Store the raster grid coordinates in the profile object - pProfile->AppendCellInProfile(nX, nY); - - CGeom2DPoint const Pt(dGridCentroidXToExtCRSX(nX), dGridCentroidYToExtCRSY(nY)); // In external CRS - - // Store the external coordinates in the profile object. Note that for this grid-edge profile, the coordinates of the cells and the coordinates of points on the profile itself are identical, this is not the case for ordinary profiles - pProfile->AppendPointInProfile(&Pt); - } - - int const nEndX = VPtiNormalPoints.back().nGetX(); - int const nEndY = VPtiNormalPoints.back().nGetY(); - - // Get the deep water wave height and orientation values at the end of the profile - double const dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveHeight(); - double const dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveAngle(); - double const dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWavePeriod(); - - // And store them in this profile - pProfile->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight); - pProfile->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle); - pProfile->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod); - - // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment - pProfile->AppendLineSegment(); - pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0)); - - // Store the grid-edge profile - m_VCoast[nCoast].AppendProfile(pProfile); - m_VCoast[nCoast].SetProfileAtCoastPoint(nProfileStartPoint, pProfile); - - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": \tcoast " << nCoast << " grid-edge profile " << nProfile << " created at coast " << (bCoastStart ? "start" : "end") << " point " << (bCoastStart ? 0 : nCoastSize - 1) << ", from [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} to [" << VPtiNormalPoints.back().nGetX() << "][" << VPtiNormalPoints.back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(VPtiNormalPoints.back().nGetX()) << ", " << dGridCentroidYToExtCRSY(VPtiNormalPoints.back().nGetY()) << "}" << endl; - - // assert(pProfile->nGetProfileSize() > 0); - - return RTN_OK; -} - -//=============================================================================================================================== -//! Finds the end point of a coastline-normal line, given the start point on the vector coastline. If however the start point is on the grid edge (only applicable to cliff collapse [rofiles), then the end point is also on the grid edge, and the line joining the start and end points is not necessarily normal to the vector coast. All input coordinates are in the external CRS -//=============================================================================================================================== -int CSimulation::nGetCoastNormalEndPoint(int const nCoast, int const nStartCoastPoint, int const nCoastSize, CGeom2DPoint const* pPtStart, double const dLineLength, CGeom2DPoint* pPtEnd, CGeom2DIPoint* pPtiEnd, bool const bIntervention) -{ - int const AVGSIZE = 21; // TODO 011 This should be a user input - - double dXEnd1 = 0; - double dXEnd2 = 0; - double dYEnd1 = 0; - double dYEnd2 = 0; - - CGeom2DPoint PtBefore; - CGeom2DPoint PtAfter; - - if (bIntervention) - { - // This is an intervention profile, so just use one point on either side (coordinates in external CRS). TODO Note this this assumes that this intervention profile is not at the start or end of the coastline - PtBefore = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint - 1); - PtAfter = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint + 1); - } - else - { - // This is not an intervention profile. It could be a cliff collapse profile, which could be a grid-edge profile - double const dXStart = pPtStart->dGetX(); - double const dYStart = pPtStart->dGetY(); - - int const nXStart = nRound(dExtCRSXToGridX(dXStart)); - int const nYStart = nRound(dExtCRSYToGridY(dYStart)); - int const nLineLength = nConvertMetresToNumCells(dLineLength); - - // LogStream << nXStart << ", " << nYStart << endl; - if ((nXStart == 0) || (nXStart == m_nXGridSize-1)) - { - // Yes it is a grid-edge profile - dXEnd1 = dGridXToExtCRSX(nXStart); - dXEnd2 = dXEnd1; - - dYEnd1 = dGridYToExtCRSY(nYStart + nLineLength); - dYEnd2 = dGridYToExtCRSY(nYStart - nLineLength); - } - else if ((nYStart == 0) || (nYStart == m_nYGridSize-1)) - { - // Yes it is a grid-edge profile - dYEnd1 = dGridYToExtCRSY(nYStart); - dYEnd2 = dYEnd1; - - dXEnd1 = dGridXToExtCRSX(nXStart + nLineLength); - dXEnd2 = dGridXToExtCRSX(nXStart - nLineLength); - } - else - { - // This is not a grid-edge profile, so put a maximum of AVGSIZE points before the start point into a vector - vector VPtBeforeToAverage; - - for (int n = 1; n <= AVGSIZE; n++) - { - int const nPoint = nStartCoastPoint - n; - if (nPoint < 0) - break; - - VPtBeforeToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint)); - } - - // Put a maximum of AVGSIZE points after the start point into a vector - vector VPtAfterToAverage; - - for (int n = 1; n <= AVGSIZE; n++) - { - int const nPoint = nStartCoastPoint + n; - if (nPoint > nCoastSize - 1) - break; - - VPtAfterToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint)); - } - - // Now average each of these vectors of points: results are in PtBefore and PtAfter (coordinates in external CRS) - PtBefore = PtAverage(&VPtBeforeToAverage); - PtAfter = PtAverage(&VPtAfterToAverage); - - // Get the y = a * x + b equation of the straight line linking the coastline points before and after 'this' coastline point. For this linking line, slope a = (y2 - y1) / (x2 - x1) - double const dYDiff = PtAfter.dGetY() - PtBefore.dGetY(); - double const dXDiff = PtAfter.dGetX() - PtBefore.dGetX(); - - if (bFPIsEqual(dYDiff, 0.0, TOLERANCE)) - { - // The linking line runs W-E or E-W, so a straight line at right angles to this runs N-S or S-N. Calculate the two possible end points for this coastline-normal profile - dXEnd1 = dXEnd2 = pPtStart->dGetX(); - dYEnd1 = pPtStart->dGetY() + dLineLength; - dYEnd2 = pPtStart->dGetY() - dLineLength; - } - else if (bFPIsEqual(dXDiff, 0.0, TOLERANCE)) - { - // The linking line runs N-S or S-N, so a straight line at right angles to this runs W-E or E-W. Calculate the two possible end points for this coastline-normal profile - dYEnd1 = dYEnd2 = pPtStart->dGetY(); - dXEnd1 = pPtStart->dGetX() + dLineLength; - dXEnd2 = pPtStart->dGetX() - dLineLength; - } - else - { - // The linking line runs neither W-E nor N-S so we have to work a bit harder to find the end-point of the coastline-normal profile - double const dA = dYDiff / dXDiff; - - // Now calculate the equation of the straight line which is perpendicular to this linking line - double const dAPerp = -1 / dA; - double const dBPerp = pPtStart->dGetY() - (dAPerp * pPtStart->dGetX()); - - // Calculate the end point of the profile: first do some substitution then rearrange as a quadratic equation i.e. in the form Ax^2 + Bx + C = 0 (see http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle) - double const dQuadA = 1 + (dAPerp * dAPerp); - double const dQuadB = 2 * ((dBPerp * dAPerp) - (dAPerp * pPtStart->dGetY()) - pPtStart->dGetX()); - double const dQuadC = ((pPtStart->dGetX() * pPtStart->dGetX()) + (pPtStart->dGetY() * pPtStart->dGetY()) + (dBPerp * dBPerp) - (2 * pPtStart->dGetY() * dBPerp) - (dLineLength * dLineLength)); - - // Solve for x and y using the quadratic formula x = (−B ± sqrt(B^2 − 4AC)) / 2A - double const dDiscriminant = (dQuadB * dQuadB) - (4 * dQuadA * dQuadC); - - if (dDiscriminant < 0) - { - LogStream << ERR << "timestep " << m_ulIter << ": discriminant < 0 when finding profile end point on coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl; - return RTN_ERR_NO_SOLUTION_FOR_ENDPOINT; - } - - dXEnd1 = (-dQuadB + sqrt(dDiscriminant)) / (2 * dQuadA); - dYEnd1 = (dAPerp * dXEnd1) + dBPerp; - dXEnd2 = (-dQuadB - sqrt(dDiscriminant)) / (2 * dQuadA); - dYEnd2 = (dAPerp * dXEnd2) + dBPerp; - } - } - } - - // We have two possible solutions, so decide which of the two endpoints to use then create the profile end-point (coordinates in external CRS) - int const nSeaHand = m_VCoast[nCoast].nGetSeaHandedness(); // Assumes handedness is either 0 or 1 (i.e. not -1) - *pPtEnd = PtChooseEndPoint(nSeaHand, &PtBefore, &PtAfter, dXEnd1, dYEnd1, dXEnd2, dYEnd2); - - // Check that pPtiEnd is not off the grid. Note that pPtiEnd is not necessarily a cell centroid - pPtiEnd->SetXY(nRound(dExtCRSXToGridX(pPtEnd->dGetX())), nRound(dExtCRSYToGridY(pPtEnd->dGetY()))); - - if (! bIsWithinValidGrid(pPtiEnd)) - { - // LogStream << m_ulIter << ": profile endpoint is outside grid [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}. The profile starts at coastline point " << nStartCoastPoint << " = {" << pPtStart->dGetX() << ", " << pPtStart->dGetY() << "}" << endl; - - // The end point is off the grid, so constrain it to be within the valid grid - CGeom2DIPoint const PtiStart(nRound(dExtCRSXToGridX(pPtStart->dGetX())), nRound(dExtCRSYToGridY(pPtStart->dGetY()))); - KeepWithinValidGrid(&PtiStart, pPtiEnd); - - pPtEnd->SetX(dGridCentroidXToExtCRSX(pPtiEnd->nGetX())); - pPtEnd->SetY(dGridCentroidYToExtCRSY(pPtiEnd->nGetY())); - - // LogStream << m_ulIter << ": \tcoast " << nCoast << " profile endpoint constrained to be within grid, is now [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}. The profile starts at coastline point " << nStartCoastPoint << " = {" << pPtStart->dGetX() << ", " << pPtStart->dGetY() << "}" << endl; - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! Choose which end point to use for the coastline-normal profile -//=============================================================================================================================== -CGeom2DPoint CSimulation::PtChooseEndPoint(int const nHand, CGeom2DPoint const* PtBefore, CGeom2DPoint const* PtAfter, double const dXEnd1, double const dYEnd1, double const dXEnd2, double const dYEnd2) -{ - CGeom2DPoint PtChosen; - - // All coordinates here are in the external CRS, so the origin of the grid is the bottom left - if (nHand == RIGHT_HANDED) - { - // The sea is to the right of the linking line. So which way is the linking line oriented? First check the N-S component - if (PtAfter->dGetY() > PtBefore->dGetY()) - { - // We are going S to N and the sea is to the right: the normal endpoint is to the E. We want the larger of the two x values - if (dXEnd1 > dXEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else if (PtAfter->dGetY() < PtBefore->dGetY()) - { - // We are going N to S and the sea is to the right: the normal endpoint is to the W. We want the smaller of the two x values - if (dXEnd1 < dXEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else - { - // No N-S component i.e. the linking line is exactly W-E. So check the W-E component - if (PtAfter->dGetX() > PtBefore->dGetX()) - { - // We are going W to E and the sea is to the right: the normal endpoint is to the s. We want the smaller of the two y values - if (dYEnd1 < dYEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident - { - // We are going E to W and the sea is to the right: the normal endpoint is to the N. We want the larger of the two y values - if (dYEnd1 > dYEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - } - } - else // nHand == LEFT_HANDED - { - // The sea is to the left of the linking line. So which way is the linking line oriented? First check the N-S component - if (PtAfter->dGetY() > PtBefore->dGetY()) - { - // We are going S to N and the sea is to the left: the normal endpoint is to the W. We want the smaller of the two x values - if (dXEnd1 < dXEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else if (PtAfter->dGetY() < PtBefore->dGetY()) - { - // We are going N to S and the sea is to the left: the normal endpoint is to the E. We want the larger of the two x values - if (dXEnd1 > dXEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else - { - // No N-S component i.e. the linking line is exactly W-E. So check the W-E component - if (PtAfter->dGetX() > PtBefore->dGetX()) - { - // We are going W to E and the sea is to the left: the normal endpoint is to the N. We want the larger of the two y values - if (dYEnd1 > dYEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident - { - // We are going E to W and the sea is to the left: the normal endpoint is to the S. We want the smaller of the two y values - if (dYEnd1 < dYEnd2) - { - PtChosen.SetX(dXEnd1); - PtChosen.SetY(dYEnd1); - } - else - { - PtChosen.SetX(dXEnd2); - PtChosen.SetY(dYEnd2); - } - } - } - } - - return PtChosen; -} - -//=============================================================================================================================== -//! Checks all coastline-normal profiles for intersection, and modifies those that intersect -//=============================================================================================================================== -void CSimulation::CheckForIntersectingProfiles(void) -{ - LogStream << endl << m_ulIter << ": Checking for profile intersection" << endl; - - // Do once for every coastline object - int const nCoastLines = static_cast(m_VCoast.size()); - - for (int nCoast = 0; nCoast < nCoastLines; nCoast++) - { - int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); - - // Do once for every profile, in along-coast sequence - for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) - { - if (! m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint)) - continue; - - // There is a profile at this coast point - CGeomProfile* pFirstProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint); - int const nFirstProfile = pFirstProfile->nGetProfileID(); - - // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however - if (! pFirstProfile->bProfileOKIncTruncated()) - { - // LogStream << m_ulIter << ": nCoastPoint = " << nCoastPoint << " pFirstProfile = " << pFirstProfile->nGetProfileID() << " is not OK (could be a start- or end-of-coast profile), abandoning" << endl; - continue; - } - - // OK we have found a first profile. Now go along the coast in alternate directions: first down-coast (in the direction of increasing coast point numbers) then up-coast - for (int nDirection = DIRECTION_DOWNCOAST; nDirection <= DIRECTION_UPCOAST; nDirection++) - { - int nStartPoint; - - if (nDirection == DIRECTION_DOWNCOAST) - nStartPoint = nCoastPoint + 1; - else - nStartPoint = nCoastPoint - 1; - - for (int nSecondCoastPoint = nStartPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint < nCoastSize : nSecondCoastPoint >= 0; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint++ : nSecondCoastPoint--) - // // In this direction, look at profiles which are increasingly close to the first profile - // int nStartPoint; - // if (nDirection == DIRECTION_DOWNCOAST) - // nStartPoint = 0; - // else - // nStartPoint = nCoastSize - 1; - // - // for (int nSecondCoastPoint = nStartPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint < nCoastPoint : nSecondCoastPoint > nCoastPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint++ : nSecondCoastPoint--) - { - if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nSecondCoastPoint)) - { - // There is a profile at the second coast point, so get a pointer to it - CGeomProfile* pSecondProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nSecondCoastPoint); - int const nSecondProfile = pSecondProfile->nGetProfileID(); - - // LogStream << m_ulIter << ": " << (nDirection == DIRECTION_DOWNCOAST ? "down" : "up") << "-coast search, nCoastPoint = " << nCoastPoint << " nSecondCoastPoint = " << nSecondCoastPoint << " (profiles " << pFirstProfile->nGetProfileID() << " and " << pSecondProfile->nGetProfileID() << ")" << endl; - - // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however - if (! pSecondProfile->bProfileOKIncTruncated()) - { - // LogStream << m_ulIter << ": second profile = " << pSecondProfile->nGetProfileID() << " is not OK (could be a start- or end-of-coast profile), abandoning" << endl; - continue; - } - - // Only check these two profiles for intersection if they are are not co-incident in the final line segment of both profiles (i.e. the profiles have not already intersected) - if ((pFirstProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nSecondProfile)) || (pSecondProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nFirstProfile))) - { - // LogStream << m_ulIter << ": profiles " << pFirstProfile->nGetProfileID() << " and " << pSecondProfile->nGetProfileID() << " are are not co-incident in the final line segment of both profiles (i.e. the profiles have not already intersected), abandoning" << endl; - continue; - } - - // OK go for it - int nProf1LineSeg = 0; - int nProf2LineSeg = 0; - double dIntersectX = 0; - double dIntersectY = 0; - double dAvgEndX = 0; - double dAvgEndY = 0; - - if (bCheckForIntersection(pFirstProfile, pSecondProfile, nProf1LineSeg, nProf2LineSeg, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY)) - { - // The profiles intersect. Decide which profile to truncate, and which to retain - int nPoint = -1; - - if (pFirstProfile->bIsIntervention()) - { - LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, truncate " << nFirstProfile << " since it is an intervention profile" << endl; - - // Truncate the first profile, since it is an intervention profile - TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); - } - else if (pSecondProfile->bIsIntervention()) - { - LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, truncate " << nSecondProfile << " since it is an intervention profile" << endl; - - // Truncate the second profile, since it is an intervention profile - TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); - } - // Is the point of intersection already present in the first profile (i.e. because there has already been an intersection at this point between the first profile and some other profile)? - else if (pFirstProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint)) - { - LogStream << m_ulIter << ": \t coast " << nCoast << " profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nFirstProfile << " as point " << nPoint << endl; - - // Truncate the second profile and merge it with the first profile - TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, true); - } - // Is the point of intersection already present in the second profile? - else if (pSecondProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint)) - { - LogStream << m_ulIter << ": \tcoast " << nCoast << " profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nSecondProfile << " as point " << nPoint << endl; - - // Truncate the first profile and merge it with the second profile - TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, true); - } - else - { - // The point of intersection is not already present in either profile, so get the number of line segments of each profile - int const nFirstProfileLineSegments = pFirstProfile->nGetNumLineSegments(); - int const nSecondProfileLineSegments = pSecondProfile->nGetNumLineSegments(); - - // assert(nProf1LineSeg < nFirstProfileLineSegments); - // assert(nProf2LineSeg < nSecondProfileLineSegments); - - // Next check whether the point of intersection is on the final line segment of both profiles - if ((nProf1LineSeg == (nFirstProfileLineSegments - 1)) && (nProf2LineSeg == (nSecondProfileLineSegments - 1))) - { - // Yes, the point of intersection is on the final line segment of both profiles, so merge the profiles seaward of the point of intersection - MergeProfilesAtFinalLineSegments(nCoast, pFirstProfile, pSecondProfile, nFirstProfileLineSegments, nSecondProfileLineSegments, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY); - - // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, end-segment intersection between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << " segments, and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << " segments, respectively" << endl; - - // // DEBUG CODE ============================================================================================= - // int nSizeTmp = pFirstProfile->nGetProfileSize(); - // CGeom2DPoint PtEndTmp = *pFirstProfile->pPtGetPointInProfile(nSizeTmp-1); - // - // LogStream << m_ulIter << ": end of first profile (" << nFirstProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl; - // - // nSizeTmp = pSecondProfile->nGetProfileSize(); - // PtEndTmp = *pSecondProfile->pPtGetPointInProfile(nSizeTmp-1); - // - // LogStream << m_ulIter << ": end of second profile (" << nSecondProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl; - // // DEBUG CODE ============================================================================================= - } - else - { - // The profiles intersect, but the point of intersection is not on the final line segment of both profiles. One of the profiles will be truncated, the other profile will be retained - // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, intersection (NOT both end segments) between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << ", and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << ", respectively" << endl; - - // Decide which profile to truncate, and which to retain - if (pFirstProfile->bIsIntervention()) - { - // Truncate the first profile, since it is an intervention profile - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pFirstProfile is an intervention profile, so truncate pFirstProfile (" << pFirstProfile->nGetProfileID() << ")" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); - } - else if (pSecondProfile->bIsIntervention()) - { - // Truncate the second profile, since it is an intervention profile - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pSecondProfile is an intervention profile, so truncate pSecondProfile (" << pSecondProfile->nGetProfileID() << ")" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); - } - else if (nFirstProfileLineSegments < nSecondProfileLineSegments) - { - // Truncate the first profile, since it has a smaller number of line segments - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pFirstProfile has a smaller number of line segments, so truncate pFirstProfile (" << pFirstProfile->nGetProfileID() << ")" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); - } - else if (nFirstProfileLineSegments > nSecondProfileLineSegments) - { - // Truncate the second profile, since it has a smaller number of line segments - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pSecondProfile has a smaller number of line segments, so truncate pSecondProfile (" << pSecondProfile->nGetProfileID() << ")" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); - } - else - { - // Both profiles have the same number of line segments, so choose randomly. Draw a sample from the unit normal distribution using random number generator 1 - double const dRand = m_dGetFromUnitNormalDist(m_Rand[0]); - - if (dRand >= 0.0) - { - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", same number of line segment, randomly truncate pFirstProfile" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); - } - else - { - // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", same number of line segment, randomly truncate pSecondProfile" << endl; - - TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); - } - } - } - } - } - } - } - } - } - } -} - -//=============================================================================================================================== -//! Check all coastline-normal profiles and modify the profiles if they intersect, then mark valid profiles on the raster grid -//=============================================================================================================================== -int CSimulation::nCheckAndMarkAllProfiles(void) -{ - // Check to see which coastline-normal profiles intersect. Then modify intersecting profiles so that the sections of each profile seaward of the point of intersection are 'shared' i.e. are multi-lines. This creates the boundaries of the triangular polygons - CheckForIntersectingProfiles(); - - // Again check the normal profiles for insufficient length: is the water depth at the end point less than the depth of closure? We do this again because some profiles may have been shortened as a result of intersection. Do once for every coastline object - for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++) - { - for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) - { - CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); - int const nProfile = pProfile->nGetProfileID(); - - if (pProfile->bProfileOK()) - { - int const nSize = pProfile->nGetProfileSize(); - - // Safety check - if (nSize == 0) - { - // pProfile->SetTooShort(true); - m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); - LogStream << "Profile " << nProfile << " is too short, size = " << nSize << endl; - continue; - } - - CGeom2DPoint const* pPtEnd = pProfile->pPtGetPointInProfile(nSize - 1); - CGeom2DIPoint const PtiEnd = PtiExtCRSToGridRound(pPtEnd); - int nXEnd = PtiEnd.nGetX(); - int nYEnd = PtiEnd.nGetY(); - - // Safety checks: the point may be outside the grid, so keep it within the grid - nXEnd = tMin(nXEnd, m_nXGridSize - 1); - nYEnd = tMin(nYEnd, m_nYGridSize - 1); - nXEnd = tMax(nXEnd, 0); - nYEnd = tMax(nYEnd, 0); - - if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure) - { - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}, flagging as too short" << endl; - - // pProfile->SetTooShort(true); - m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); - } - } - } - - // For this coast, put all valid coastline-normal profiles (apart from the profiles at the start and end of the coast, since they have already been done) onto the raster grid. But if the profile is not long enough, crosses a coastline, hits dry land, or hits another profile, then mark the profile as invalid - int nValidProfiles = 0; - MarkProfilesOnGrid(nCoast, nValidProfiles); - - if (nValidProfiles == 0) - { - // Problem! No valid profiles, so quit - cerr << m_ulIter << ": " << ERR << "no coastline-normal profiles created" << endl; - return RTN_ERR_NO_PROFILES_2; - } - - // // DEBUG CODE =========================================================================================================== - // if (m_ulIter == 109) - // { - // string strOutFile = m_strOutPath; - // strOutFile += "00_profile_raster_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // - // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); - // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // - // int nn = 0; - // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // if (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()) - // pdRaster[nn] = -1; - // else - // { - // - // // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); - // } - // - // nn++; - // } - // } - // - // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_dMissingValue); - // int nRet1 = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet1 == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // } - // // DEBUG CODE =========================================================================================================== - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! Checks all line segments of a pair of coastline-normal profiles for intersection. If the lines intersect, returns true with the numbers of the line segments at which intersection occurs in nProfile1LineSegment and nProfile1LineSegment, the intersection point in dXIntersect and dYIntersect, and the 'average' seaward endpoint of the two intersecting profiles at dXAvgEnd and dYAvgEnd -//=============================================================================================================================== -bool CSimulation::bCheckForIntersection(CGeomProfile* const pVProfile1, CGeomProfile* const pVProfile2, int& nProfile1LineSegment, int& nProfile2LineSegment, double& dXIntersect, double& dYIntersect, double& dXAvgEnd, double& dYAvgEnd) -{ - // For both profiles, look at all line segments - int const nProfile1NumSegments = pVProfile1->nGetNumLineSegments(); - int const nProfile2NumSegments = pVProfile2->nGetNumLineSegments(); - // nProfile1Size = pVProfile1->nGetProfileSize(), - // nProfile2Size = pVProfile2->nGetProfileSize(); - - // assert(nProfile1Size == nProfile1NumSegments+1); - // assert(nProfile2Size == nProfile2NumSegments+1); - - for (int i = 0; i < nProfile1NumSegments; i++) - { - for (int j = 0; j < nProfile2NumSegments; j++) - { - // In external coordinates - double const dX1 = pVProfile1->pPtVGetPoints()->at(i).dGetX(); - double const dY1 = pVProfile1->pPtVGetPoints()->at(i).dGetY(); - double const dX2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetX(); - double const dY2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetY(); - - double const dX3 = pVProfile2->pPtVGetPoints()->at(j).dGetX(); - double const dY3 = pVProfile2->pPtVGetPoints()->at(j).dGetY(); - double const dX4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetX(); - double const dY4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetY(); - - // Uses Cramer's Rule to solve the equations. Modified from code at http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect (in turn based on Andre LeMothe's "Tricks of the Windows Game Programming Gurus") - double const dDiffX1 = dX2 - dX1; - double const dDiffY1 = dY2 - dY1; - double const dDiffX2 = dX4 - dX3; - double const dDiffY2 = dY4 - dY3; - - double dS = -999; - double dT = -999; - double dTmp = 0; - - dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2; - - if (! bFPIsEqual(dTmp, 0.0, TOLERANCE)) - dS = (-dDiffY1 * (dX1 - dX3) + dDiffX1 * (dY1 - dY3)) / dTmp; - - dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2; - - if (! bFPIsEqual(dTmp, 0.0, TOLERANCE)) - dT = (dDiffX2 * (dY1 - dY3) - dDiffY2 * (dX1 - dX3)) / dTmp; - - if (dS >= 0 && dS <= 1 && dT >= 0 && dT <= 1) - { - // Collision detected, calculate intersection coordinates - dXIntersect = dX1 + (dT * dDiffX1); - dYIntersect = dY1 + (dT * dDiffY1); - - // And calc the average end-point coordinates - dXAvgEnd = (dX2 + dX4) / 2; - dYAvgEnd = (dY2 + dY4) / 2; - - // Get the line segments at which intersection occurred - nProfile1LineSegment = i; - nProfile2LineSegment = j; - - // LogStream << "\t" << "INTERSECTION dX2 = " << dX2 << " dX4 = " << dX4 << " dY2 = " << dY2 << " dY4 = " << dY4 << endl; - return true; - } - } - } - - // No intersection - return false; -} - -//=============================================================================================================================== -//! For this coastline, marks all coastline-normal profiles (apart from the two 'special' ones at the start and end of the coast) onto the raster grid, i.e. rasterizes multi-line vector objects onto the raster grid. Note that this doesn't work if the vector has already been interpolated to fit on the grid i.e. if distances between vector points are just one cell apart -//=============================================================================================================================== -void CSimulation::MarkProfilesOnGrid(int const nCoast, int& nValidProfiles) -{ - // How many profiles on this coast? - int const nProfiles = m_VCoast[nCoast].nGetNumProfiles(); - - if (nProfiles == 0) - { - // This can happen if the coastline is very short, so just give a warning and carry on with the next coastline - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << WARN << m_ulIter << ": coast " << nCoast << " has no profiles" << endl; - - return; - } - - static bool bDownCoast = true; - - // Now do this for every profile, alternate between up-coast and down-coast directions - for (int n = 0; n < nProfiles; n++) - { - CGeomProfile* pProfile; - - if (bDownCoast) - pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); - else - pProfile = m_VCoast[nCoast].pGetProfileWithUpCoastSeq(n); - - // Don't do this for the first and last profiles (i.e. the profiles at the start and end of the coast) since these are put onto the grid elsewhere - if (pProfile->bIsGridEdge()) - continue; - - int const nProfile = pProfile->nGetProfileID(); - - // If this profile has a problem, then forget about it - // if (! pProfile->bProfileOK()) - // { - // LogStream << m_ulIter << ": in MarkProfilesOnGrid() profile " << nProfile << " is not OK" << endl; - // continue; - // } - - int const nPoints = pProfile->nGetProfileSize(); - - if (nPoints < 2) - { - // Need at least two points in the profile, so this profile is invalid: mark it - m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); - - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, has only " << nPoints << " points" << endl; - - continue; - } - - // OK, go for it: set up temporary vectors to hold the x-y coords (in grid CRS) of the cells which we will mark - vector VCellsToMark; - vector bVShared; - bool bTooShort = false; - bool bTruncatedSameCoast = false; - bool bHitCoast = false; - bool bHitLand = false; - bool bHitIntervention = false; - bool bHitAnotherProfile = false; - - CreateRasterizedProfile(nCoast, pProfile, &VCellsToMark, &bVShared, bTooShort, bTruncatedSameCoast, bHitCoast, bHitLand, bHitIntervention, bHitAnotherProfile); - - if ((bTruncatedSameCoast && (! ACCEPT_TRUNCATED_PROFILES)) || bTooShort || bHitCoast || bHitLand || bHitIntervention || bHitAnotherProfile || VCellsToMark.size() == 0) - continue; - - // This profile is fine - nValidProfiles++; - - for (unsigned int k = 0; k < VCellsToMark.size(); k++) - { - // Ignore duplicate points - if ((k > 0) && (VCellsToMark[k] == m_VCoast[nCoast].pGetProfile(nProfile)->pPtiGetLastCellInProfile())) - continue; - - // Mark each cell in the raster grid - int const nXTmp = VCellsToMark[k].nGetX(); - int const nYTmp = VCellsToMark[k].nGetY(); - m_pRasterGrid->m_Cell[nXTmp][nYTmp].SetCoastAndProfileID(nCoast, nProfile); - - // Store the raster grid coordinates in the profile object - m_VCoast[nCoast].pGetProfile(nProfile)->AppendCellInProfile(nXTmp, nYTmp); - - // Mark the shared (i.e. multi-line) parts of the profile (if any) - // if (bVShared[k]) - // { - // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(true); - // // LogStream << m_ulIter << ": profile " << j << " point " << k << " marked as shared" << endl; - // } - // else - // { - // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(false); - // // LogStream << m_ulIter << ": profile " << nProfile << " point " << k << " marked as NOT shared" << endl; - // } - } - - // Get the deep water wave height and orientation values at the end of the profile - double const dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveHeight(); - double const dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveAngle(); - double const dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWavePeriod(); - - // And store them for this profile - m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight); - m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle); - m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod); - } - - bDownCoast = ! bDownCoast; -} - -//=============================================================================================================================== -//! Given a pointer to a coastline-normal profile, returns an output vector of cells which are 'under' every line segment of the profile. If there is a problem with the profile (e.g. a rasterized cell is dry land or coast, or the profile has to be truncated) then we pass this back as an error code -//=============================================================================================================================== -void CSimulation::CreateRasterizedProfile(int const nCoast, CGeomProfile* pProfile, vector* pVIPointsOut, vector* pbVShared, bool& bTooShort, bool& bTruncatedSameCoast, bool& bHitCoast, bool& bHitLand, bool& bHitIntervention, bool& bHitAnotherProfile) -{ - int const nProfile = pProfile->nGetProfileID(); - int nSeg = 0; - int const nNumSegments = pProfile->nGetNumLineSegments(); - - pVIPointsOut->clear(); - - // LogStream << m_ulIter << ": in CreateRasterizedProfile() *pPtiStart for profile " << nProfile << " is [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "]" << endl; - int nXStartLast = INT_NODATA; - int nYStartLast = INT_NODATA; - int nXEndLast = INT_NODATA; - int nYEndLast = INT_NODATA; - - // Do for every segment of this profile - for (nSeg = 0; nSeg < nNumSegments; nSeg++) - { - // Do once for every line segment - CGeom2DIPoint PtiSegStart; - - if (nSeg == 0) - { - // If this is the first segment, use the coastline start point to prevent external CRS to grid CRS rounding errors - int const nCoastPoint = pProfile->nGetCoastPoint(); - PtiSegStart = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastPoint); - } - else - { - CGeom2DPoint const* pPtSegStart = pProfile->pPtGetPointInProfile(nSeg); - - // Convert from the external CRS to grid CRS - PtiSegStart = PtiExtCRSToGridRound(pPtSegStart); - } - - CGeom2DPoint const* pPtSegEnd = pProfile->pPtGetPointInProfile(nSeg + 1); // This is OK - - // Convert from the external CRS to grid CRS - CGeom2DIPoint const PtiSegEnd = PtiExtCRSToGridRound(pPtSegEnd); - - // Safety check - if (PtiSegStart == PtiSegEnd) - continue; - - int const nXStart = PtiSegStart.nGetX(); - int const nYStart = PtiSegStart.nGetY(); - int const nXEnd = PtiSegEnd.nGetX(); - int const nYEnd = PtiSegEnd.nGetY(); - - bool bShared = false; - - if (pProfile->nGetNumCoincidentProfilesInLineSegment(nSeg) > 1) - { - bShared = true; - - // If this is the second or more of several coincident line segments (i.e. it has the same start and end points as the previous line segment) then ignore it - if ((nXStart == nXStartLast) && (nYStart == nYStartLast) && (nXEnd == nXEndLast) && (nYEnd == nYEndLast)) - continue; - } - - // Interpolate between cells by a simple DDA line algorithm, see http://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm) Note that Bresenham's algorithm gave occasional gaps - double dXInc = nXEnd - nXStart; - double dYInc = nYEnd - nYStart; - double const dLength = tMax(tAbs(dXInc), tAbs(dYInc)); - - dXInc /= dLength; - dYInc /= dLength; - - double dX = nXStart; - double dY = nYStart; - - // Process each interpolated point - for (int m = 0; m <= nRound(dLength); m++) - { - int const nX = nRound(dX); - int const nY = nRound(dY); - - // Do some checking of this interpolated point, but only if this is not a grid-edge profile (these profiles are always valid) - if (! pProfile->bIsGridEdge()) - { - // Is the interpolated point within the valid raster grid? - if (! bIsWithinValidGrid(nX, nY)) - { - // It is outside the valid grid, so mark this profile and quit the loop - bTruncatedSameCoast = true; - - if (! ACCEPT_TRUNCATED_PROFILES) - pProfile->SetTruncatedSameCoast(true); - - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter << ": profile " << nProfile << " is invalid, truncated at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl; - - break; - } - - // Check again: is this cell (or an adjacent cell: does not matter which) already marked as 'under' a profile? - int nYTmp = nY+1; - if (nY+1 >= m_nYGridSize) - nYTmp = nY-1; - - if (m_pRasterGrid->m_Cell[nX][nY].bIsProfile() || m_pRasterGrid->m_Cell[nX][nYTmp].bIsProfile()) - { - // This cell or an adjacent cell, is 'under' a profile, so now check if the profile belongs to another coast - int const nHitProfileCoast1 = m_pRasterGrid->m_Cell[nX][nY].nGetProfileCoastID(); - int const nHitProfileCoast2 = m_pRasterGrid->m_Cell[nX][nYTmp].nGetProfileCoastID(); - - if ((nHitProfileCoast1 == nCoast) || (nHitProfileCoast2 == nCoast)) - { - // The profile belongs to the same coast, mark this profile as invalid - bHitAnotherProfile = true; - pProfile->SetHitAnotherProfile(true); - return; - } - } - - // If this is the first line segment of the profile, then once we are clear of the coastline (when m > PROFILE_CHECK_DIST_FROM_COAST), check if this profile hits land at this interpolated point. NOTE Get problems here since if the coastline vector has been heavily smoothed, this can result is 'false positives' profiles marked as invalid which are not actually invalid, because the profile hits land when m = 0 or m = 1. This results in some cells being flagged as profile cells which are actually inland - if (m > PROFILE_CHECK_DIST_FROM_COAST) - { - // Check this cell. Two diagonal(ish) raster lines can cross each other without any intersection, so must also test an adjacent cell for intersection (does not matter which adjacent cell) - if ((m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()) || (bIsWithinValidGrid(nX, nY + 1) && m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline())) - { - // We've hit a coastline so set a switch and mark the profile, then quit - bHitCoast = true; - pProfile->SetHitCoast(true); - int const nHitCoast = m_pRasterGrid->m_Cell[nX][nY].nGetCoastline(); - - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " is invalid, hit coast " << nHitCoast << " at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl; - - return; - } - - if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) - { - // We've hit dry land, so set a switch and mark the profile - bHitLand = true; - pProfile->SetHitLand(true); - - LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " HIT LAND at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() << ", SWL = " << m_dThisIterSWL << endl; - - return; - } - - if (m_pRasterGrid->m_Cell[nX][nY].nGetInterventionClass() != INT_NODATA) - { - // We've hit an intervention, so set a switch and mark the profile - bHitIntervention = true; - pProfile->SetHitIntervention(true); - - LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " HIT INTERVENTION at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() << ", SWL = " << m_dThisIterSWL << endl; - - return; - } - } - - // Now check to see if we hit another profile which is not a coincident normal to this normal - if (m_pRasterGrid->m_Cell[nX][nY].bIsProfile()) - { - // We've hit a raster cell which is already marked as 'under' a normal profile. Get the number of the profile which marked this cell, and the coast to hich this profile belongs - int const nHitProfile = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); - int const nHitProfileCoast = m_pRasterGrid->m_Cell[nX][nY].nGetProfileCoastID(); - - // Do both profiles belong to the same coast? - if (nCoast == nHitProfileCoast) - { - // Both profiles belong to the same coast. Is this the number of a coincident profile of this profile? - if (! pProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nHitProfile)) - { - // It isn't a coincident profile, so we have just hit an unrelated profile. Mark this profile as invalid and move on - pProfile->SetHitAnotherProfile(true); - bHitAnotherProfile = true; - - return; - } - } - } - } - - // Append this point to the output vector - pVIPointsOut->push_back(CGeom2DIPoint(nX, nY)); // Is in raster grid coordinates - pbVShared->push_back(bShared); - - // And increment for next time - dX += dXInc; - dY += dYInc; - } - - nXStartLast = nXStart; - nYStartLast = nYStart; - nXEndLast = nXEnd; - nYEndLast = nYEnd; - - if (bTruncatedSameCoast) - break; - } - - if (bTruncatedSameCoast) - { - if (nSeg < (nNumSegments - 1)) - // We are truncating the profile, so remove any line segments after this one - pProfile->TruncateLineSegments(nSeg); - - // Shorten the vector input. Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty - int const nLastX = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX(); - int const nLastY = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY(); - - pProfile->pPtGetPointInProfile(nSeg + 1)->SetX(dGridCentroidXToExtCRSX(nLastX)); - pProfile->pPtGetPointInProfile(nSeg + 1)->SetY(dGridCentroidYToExtCRSY(nLastY)); - } - - // // DEBUG CODE ===================================================================================== - // LogStream << "====================" << endl; - // LogStream << m_ulIter << ": for profile " << nProfile << " pPtiStart = [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] pPtiEnd = [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] pVIPointsOut->size() = " << pVIPointsOut->size() << endl; - // // for (int n = 0; n < static_cast(pVIPointsOut->size()); n++) - // // LogStream << "\t[" << pVIPointsOut->at(n).nGetX() << "][" << pVIPointsOut->at(n).nGetY() << "]" << endl; - // LogStream << "====================" << endl; - // // DEBUG CODE ===================================================================================== - - if (pVIPointsOut->size() < 3) - { - // Coastline-normal profiles cannot be very short (e.g. with less than 3 cells), since we cannot calculate along-profile slope properly for such short profiles - bTooShort = true; - pProfile->SetTooShort(true); - - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - { - // Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty - LogStream << m_ulIter << ": profile " << nProfile << " is invalid, is too short, only " << pVIPointsOut->size() << " points, HitLand?" << bHitLand << ". From [" << pVIPointsOut->at(0).nGetX() << "][" << pVIPointsOut->at(0).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(0).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(0).nGetY()) << "} to [" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX() << "][" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY()) << "}" << endl; - } - } -} - -//=============================================================================================================================== -//! Merges two profiles which intersect at their final (most seaward) line segments, seaward of their point of intersection -//=============================================================================================================================== -void CSimulation::MergeProfilesAtFinalLineSegments(int const nCoast, CGeomProfile* pFirstProfile, CGeomProfile* pSecondProfile, int const nFirstProfileLineSegments, int const nSecondProfileLineSegments, double const dIntersectX, double const dIntersectY, double const dAvgEndX, double const dAvgEndY) -{ - // The point of intersection is on the final (most seaward) line segment of both profiles. Put together a vector of coincident profile numbers (with no duplicates) for both profiles - int nCombinedLastSeg = 0; - vector> prVCombinedProfilesCoincidentProfilesLastSeg; - - for (unsigned int n = 0; n < pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->size(); n++) - { - pair prTmp; - prTmp.first = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).first; - prTmp.second = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).second; - - bool bFound = false; - - for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++) - { - if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first) - { - bFound = true; - break; - } - } - - if (! bFound) - { - prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp); - nCombinedLastSeg++; - } - } - - for (unsigned int n = 0; n < pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->size(); n++) - { - pair prTmp; - prTmp.first = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).first; - prTmp.second = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).second; - - bool bFound = false; - - for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++) - { - if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first) - { - bFound = true; - break; - } - } - - if (! bFound) - { - prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp); - nCombinedLastSeg++; - } - } - - // Increment the number of each line segment - for (int m = 0; m < nCombinedLastSeg; m++) - prVCombinedProfilesCoincidentProfilesLastSeg[m].second++; - - vector> prVFirstProfileCoincidentProfilesLastSeg = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1); - vector> prVSecondProfileCoincidentProfilesLastSeg = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1); - int const nNumFirstProfileCoincidentProfilesLastSeg = static_cast(prVFirstProfileCoincidentProfilesLastSeg.size()); - int const nNumSecondProfileCoincidentProfilesLastSeg = static_cast(prVSecondProfileCoincidentProfilesLastSeg.size()); - - // LogStream << m_ulIter << ": END-SEGMENT INTERSECTION between profiles " << nFirstProfile << " and " << nSecondProfile << " at line segment " << nFirstProfileLineSegments-1 << "/" << nFirstProfileLineSegments-1 << ", and line segment " << nSecondProfileLineSegments-1 << "/" << nSecondProfileLineSegments-1 << ", respectively. Both truncated at [" << dIntersectX << ", " << dIntersectY << "] then profiles {" << nFirstProfile << "} and {" << nSecondProfile << "} extended to [" << dAvgEndX << ", " << dAvgEndY << "]" << endl; - - // Truncate the first profile, and all co-incident profiles, at the point of intersection - for (int n = 0; n < nNumFirstProfileCoincidentProfilesLastSeg; n++) - { - int const nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[n].first; - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - int const nProfileLength = pThisProfile->nGetProfileSize(); - - // This is the final line segment of the first 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile - pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY); - } - - // Truncate the second profile, and all co-incident profiles, at the point of intersection - for (int n = 0; n < nNumSecondProfileCoincidentProfilesLastSeg; n++) - { - int const nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[n].first; - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - int const nProfileLength = pThisProfile->nGetProfileSize(); - - // This is the final line segment of the second 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile - pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY); - } - - // Append a new straight line segment to the existing line segment(s) of the first profile, and to all co-incident profiles - for (int nThisLineSeg = 0; nThisLineSeg < nNumFirstProfileCoincidentProfilesLastSeg; nThisLineSeg++) - { - int const nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[nThisLineSeg].first; - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - - // Update this profile - pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY); - - // Append details of the combined profiles - pThisProfile->AppendLineSegment(); - - for (int m = 0; m < nCombinedLastSeg; m++) - pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]); - } - - // Append a new straight line segment to the existing line segment(s) of the second profile, and to all co-incident profiles - for (int nThisLineSeg = 0; nThisLineSeg < nNumSecondProfileCoincidentProfilesLastSeg; nThisLineSeg++) - { - int const nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[nThisLineSeg].first; - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - - // Update this profile - pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY); - - // Append details of the combined profiles - pThisProfile->AppendLineSegment(); - - for (int m = 0; m < nCombinedLastSeg; m++) - pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]); - } - - // // DEBUG CODE **************************************************************** - // int nFirstProfileLineSeg= pFirstProfile->nGetNumLineSegments(); - // int nSecondProfileLineSeg = pSecondProfile->nGetNumLineSegments(); - // - // LogStream << "\tProfile {" << nFirstProfile << "} now has " << nFirstProfileLineSeg << " line segments" << endl; - // for (int m = 0; m < nFirstProfileLineSeg; m++) - // { - // vector > prVCoincidentProfiles = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(m); - // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nFirstProfile << "} are {"; - // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++) - // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] "; - // LogStream << " }" << endl; - // } - // LogStream << "\tProfile {" << nSecondProfile << "} now has " << nSecondProfileLineSeg << " line segments" << endl; - // for (int m = 0; m < nSecondProfileLineSeg; m++) - // { - // vector > prVCoincidentProfiles = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(m); - // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nSecondProfile << "} are {"; - // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++) - // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] "; - // LogStream << " }" << endl; - // } - // // DEBUG CODE ****************************************************************** -} - -//=============================================================================================================================== -//! Truncates one intersecting profile at the point of intersection, and retains the other profile -//=============================================================================================================================== -void CSimulation::TruncateOneProfileRetainOtherProfile(int const nCoast, CGeomProfile* pProfileToTruncate, CGeomProfile* pProfileToRetain, double dIntersectX, double dIntersectY, int nProfileToTruncateIntersectLineSeg, int nProfileToRetainIntersectLineSeg, bool const bAlreadyPresent) -{ - // // Occasionally, profiles cross each other, with the crossing not detected. So check for intersection between pProfileToTruncate and all profiles (starting from the last i.e. in reverse order) in all segments (starting from the last i.e. in reverse order) of pProfileToRetain's CGeomMultiLine - // bool bFound = false; - // int nNumSegProfRetain = pProfileToRetain->nGetNumLineSegments(); - // for (int nSeg = nNumSegProfRetain-1; nSeg >= 0; nSeg--) - // { - // if (bFound) - // break; - // - // int nNumProfInSeg = pProfileToRetain->nGetNumCoincidentProfilesInLineSegment(nSeg); - // for (int nProf = nNumProfInSeg-1; nProf >= 0; nProf--) - // { - // int nThisProf = pProfileToRetain->nGetCoincidentProfileForLineSegment(nSeg, nProf); - // CGeomProfile* pThisProf = m_VCoast[nCoast].pGetProfile(nThisProf); - // - // int nProfToTruncLineSeg = 0; - // int nThisProfLineSeg = 0; - // double dTmpIntersectX = 0; - // double dTmpIntersectY = 0; - // double dAvgEndX = 0; - // double dAvgEndY = 0; - // - // if (bCheckForIntersection(pProfileToTruncate, pThisProf, nProfToTruncLineSeg, nThisProfLineSeg, dTmpIntersectX, dTmpIntersectY, dAvgEndX, dAvgEndY)) - // { - // // An intersection was found: so the profile with which pProfileToTruncate intersects becomes the new pProfileToRetain, and dIntersectX, dIntersectY, nProfileToTruncateIntersectLineSeg, and nProfileToRetainIntersectLineSeg are also changed - // pProfileToRetain = pThisProf; - // dIntersectX = dTmpIntersectX; - // dIntersectY = dTmpIntersectY; - // nProfileToRetainIntersectLineSeg = nThisProfLineSeg; - // nProfileToTruncateIntersectLineSeg = nProfToTruncLineSeg; - // - // bFound = true; - // break; - // } - // } - // } - - // Insert the intersection point into the main retain-profile if it is not already in the profile, and do the same for all co-incident profiles of the main retain-profile. Also add details of the to-truncate profile (and all its coincident profiles) to every line segment of the main to-retain profile which is seaward of the point of intersection - int const nRet = nInsertPointIntoProfilesIfNeededThenUpdate(nCoast, pProfileToRetain, dIntersectX, dIntersectY, nProfileToRetainIntersectLineSeg, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, bAlreadyPresent); - - if (nRet != RTN_OK) - { - // LogStream << m_ulIter << ": error in nInsertPointIntoProfilesIfNeededThenUpdate()" << endl; - return; - } - - // Get all profile points of the main retain-profile seawards from the intersection point, and do the same for the corresponding line segments (including coincident profiles). This also includes details of the main to-truncate profile (and all its coincident profiles) - vector PtVProfileLastPart; - vector>> prVLineSegLastPart; - - if (bAlreadyPresent) - { - PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg); - prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg); - } - - else - { - PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg + 1); - prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg + 1); - } - - // assert(PtVProfileLastPart.size() > 1); - // assert(prVLineSegLastPart.size() > 0); - - // Truncate the truncate-profile at the point of intersection, and do the same for all its co-incident profiles. Then append the profile points of the main to-retain profile seaward from the intersection point, and do the same for the corresponding line segments (including coincident profiles) - TruncateProfileAndAppendNew(nCoast, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, &PtVProfileLastPart, &prVLineSegLastPart); - - // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetProfileSize() > 1); - // assert(pProfileToRetain->nGetNumLineSegments() > 0); - // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetNumLineSegments() > 0); -} - -//=============================================================================================================================== -//! Inserts an intersection point into the profile that is to be retained, if that point is not already present in the profile, then does the same for all co-incident profiles. Finally adds the numbers of the to-truncate profile (and all its coincident profiles) to the seaward line segments of the to-retain profile and all its coincident profiles -//=============================================================================================================================== -int CSimulation::nInsertPointIntoProfilesIfNeededThenUpdate(int const nCoast, CGeomProfile* pProfileToRetain, double const dIntersectX, double const dIntersectY, int const nProfileToRetainIntersectLineSeg, CGeomProfile* pProfileToTruncate, int const nProfileToTruncateIntersectLineSeg, bool const bAlreadyPresent) -{ - // // DEBUG CODE **************************************************************** - // // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurred - // vector > prVRetainCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); - // int nNumRetainCoincidentCHECK1 = prVRetainCoincidentProfilesCHECK1.size(); - // for (int nn = 0; nn < nNumRetainCoincidentCHECK1; nn++) - // { - // int nThisProfile = prVRetainCoincidentProfilesCHECK1[nn].first; - // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) - // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; - // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; - // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) - // { - // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); - // LogStream << "{ "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // - // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) - // { - // CGeom2DPoint - // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), - // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); - // - // if (Pt1 == Pt2) - // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; - // } - // } - // - // // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred - // vector > prVTruncateCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg); - // int nNumTruncateCoincidentCHECK1 = prVTruncateCoincidentProfilesCHECK1.size(); - // for (int nn = 0; nn < nNumTruncateCoincidentCHECK1; nn++) - // { - // int nThisProfile = prVTruncateCoincidentProfilesCHECK1[nn].first; - // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToTruncate ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) - // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; - // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; - // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) - // { - // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); - // LogStream << "{ "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // - // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) - // { - // CGeom2DPoint - // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), - // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); - // - // if (Pt1 == Pt2) - // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; - // } - // } - // // DEBUG CODE ****************************************************************** - - int const nProfileToRetain = pProfileToRetain->nGetProfileID(); - - // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurs - vector> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); - int const nNumCoincident = static_cast(prVCoincidentProfiles.size()); - vector nLineSegAfterIntersect(nNumCoincident, -1); // The line segment after the point of intersection, for each co-incident profile - - // Do this for the main profile and all profiles which are co-incident for this line segment - for (int nn = 0; nn < nNumCoincident; nn++) - { - int const nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile - int const nThisLineSeg = prVCoincidentProfiles[nn].second; // The line segment of this profile - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - - // Is the intersection point already present in the to-retain profile? - if (! bAlreadyPresent) - { - // It is not already present, so insert it and also update the associated multi-line - if (! pThisProfile->bInsertIntersection(dIntersectX, dIntersectY, nThisLineSeg)) - { - // Error - LogStream << WARN << m_ulIter << ": cannot insert a line segment after the final line segment (" << nThisLineSeg << ") for " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile (" << nThisProfile << "), abandoning" << endl; - - return RTN_ERR_CANNOT_INSERT_POINT; - } - - // LogStream << "\tIntersection point NOT already in " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile {" << nThisProfile << "}, inserted it as point " << nThisLineSeg+1 << endl; - } - - // Get the line segment after intersection - nLineSegAfterIntersect[nn] = nThisLineSeg + 1; - } - - // for (int nn = 0; nn < nNumCoincident; nn++) - // LogStream << "\tFor profile " << prVCoincidentProfiles[nn].first << " line segment [" << nLineSegAfterIntersect[nn] << "] is immediately after the intersection point" << endl; - - // Get the coincident profiles for the to-truncate profile, at the line segment where intersection occurs - vector> prVToTruncateCoincidentProfiles = *pProfileToTruncate->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg); - int const nNumToTruncateCoincident = static_cast(prVToTruncateCoincidentProfiles.size()); - - // Now add the number of the to-truncate profile, and all its coincident profiles, to all line segments which are seaward of the point of intersection. Do this for the main profile and all profiles which are co-incident for this line segment - for (int nn = 0; nn < nNumCoincident; nn++) - { - int const nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - - // Get the number of line segments for this to-retain profile (will have just increased, if we just inserted a point) - int const nNumLineSegs = pThisProfile->nGetNumLineSegments(); - - // Do for all line segments seaward of the point of intersection - for (int nLineSeg = nLineSegAfterIntersect[nn], nIncr = 0; nLineSeg < nNumLineSegs; nLineSeg++, nIncr++) - { - // // This can happen occasionally - // if (nThisProfile == nProfileToTruncateIntersectLineSeg) - // { - // LogStream << "\t*** ERROR nThisProfile = " << nThisProfile << " nProfileToTruncateIntersectLineSeg = " << nProfileToTruncateIntersectLineSeg << ", ignoring" << endl; - // pThisProfile->SetHitAnotherProfile(true); - // continue; - // } - - // Add the number of the to-truncate profile, and all its coincident profiles, to this line segment - for (int m = 0; m < nNumToTruncateCoincident; m++) - { - int const nProfileToAdd = prVToTruncateCoincidentProfiles[m].first; - int const nProfileToAddLineSeg = prVToTruncateCoincidentProfiles[m].second; - - // LogStream << "\tAdding " << (nProfileToAdd == nProfileToTruncateIntersectLineSeg ? "main" : "co-incident") << " truncate-profile " << nProfileToAdd << ", line segment [" << nProfileToAddLineSeg + nIncr << "] to line segment " << nLineSeg << " of " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " to-retain profile " << nThisProfile << endl; - - pThisProfile->AddCoincidentProfileToExistingLineSegment(nLineSeg, nProfileToAdd, nProfileToAddLineSeg + nIncr); - } - } - } - - // // DEBUG CODE **************************************************************** - // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred - // vector > prVCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nProfileToRetain)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); - // int nNumCoincidentCHECK2 = prVCoincidentProfilesCHECK2.size(); - // for (int nn = 0; nn < nNumCoincidentCHECK2; nn++) - // { - // int nThisProfile = prVCoincidentProfilesCHECK2[nn].first; - // LogStream << "\tAFTER nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToRetain ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) - // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; - // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; - // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++) - // { - // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg); - // LogStream << "{ "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // - // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) - // { - // CGeom2DPoint - // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), - // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); - // - // if (Pt1 == Pt2) - // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; - // } - // } - // // DEBUG CODE ****************************************************************** - - return RTN_OK; -} - -//=============================================================================================================================== -//! Truncate a profile at the point of intersection, and do the same for all its co-incident profiles -//=============================================================================================================================== -void CSimulation::TruncateProfileAndAppendNew(int const nCoast, CGeomProfile* pProfileToRetain, int const nMainProfileIntersectLineSeg, vector const* pPtVProfileLastPart, vector>> const* pprVLineSegLastPart) -{ - // // DEBUG CODE **************************************************************** - // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred - // vector > prVCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); - // int nNumCoincidentCHECK1 = prVCoincidentProfilesCHECK1.size(); - // - // LogStream << "\tTruncating profile {" << nMainProfile << "}, intersection is at [" << dIntersectX << ", " << dIntersectY << "] in line segment " << nMainProfileIntersectLineSeg << endl; - // for (int nn = 0; nn < nNumCoincidentCHECK1; nn++) - // { - // int nThisProfile = prVCoincidentProfilesCHECK1[nn].first; - // LogStream << "\tBEFORE TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ("; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) - // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; - // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are "; - // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++) - // { - // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg); - // LogStream << "{ "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // - // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) - // { - // CGeom2DPoint - // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), - // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); - // - // if (Pt1 == Pt2) - // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; - // } - // } - // LogStream << "\tPart-profile to append is "; - // for (int mm = 0; mm < pPtVProfileLastPart->size(); mm++) - // LogStream << "[" << pPtVProfileLastPart->at(mm).dGetX() << ", " << pPtVProfileLastPart->at(mm).dGetY() << "] "; - // LogStream << endl; - // LogStream << "\tPart line-segment to append is "; - // for (int mm = 0; mm < pprVLineSegLastPart->size(); mm++) - // { - // vector > prVTmp = pprVLineSegLastPart->at(mm); - // LogStream << "{ "; - // for (int nn = 0; nn < prVTmp.size(); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // // DEBUG CODE ****************************************************************** - - // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurs - vector> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); - int const nNumCoincident = static_cast(prVCoincidentProfiles.size()); - - for (int nn = 0; nn < nNumCoincident; nn++) - { - // Do this for the main to-truncate profile, and do the same for all its co-incident profiles - int const nThisProfile = prVCoincidentProfiles[nn].first; - int const nThisProfileLineSeg = prVCoincidentProfiles[nn].second; - CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); - - // if (nThisProfile == nMainProfile) - // assert(nThisProfileLineSeg == nMainProfileIntersectLineSeg); - - // Truncate the profile - // LogStream << "\tTruncating " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} at line segment " << nThisProfileLineSeg+1 << endl; - pThisProfile->TruncateProfile(nThisProfileLineSeg + 1); - - // Reduce the number of line segments for this profile - pThisProfile->TruncateLineSegments(nThisProfileLineSeg + 1); - - // Append the profile points from the last part of the retain-profile - for (unsigned int mm = 0; mm < pPtVProfileLastPart->size(); mm++) - { - CGeom2DPoint const Pt = pPtVProfileLastPart->at(mm); - pThisProfile->AppendPointInProfile(&Pt); - } - - // Append the line segments, and their co-incident profile numbers, from the last part of the retain-profile - for (unsigned int mm = 0; mm < pprVLineSegLastPart->size(); mm++) - { - vector> prVTmp = pprVLineSegLastPart->at(mm); - - pThisProfile->AppendLineSegment(&prVTmp); - } - - // Fix the line seg numbers for this profile - vector nVProf; - vector nVProfsLineSeg; - - for (int nSeg = 0; nSeg < pThisProfile->nGetNumLineSegments(); nSeg++) - { - for (int nCoinc = 0; nCoinc < pThisProfile->nGetNumCoincidentProfilesInLineSegment(nSeg); nCoinc++) - { - int const nProf = pThisProfile->nGetProf(nSeg, nCoinc); - int const nProfsLineSeg = pThisProfile->nGetProfsLineSeg(nSeg, nCoinc); - - auto it = find(nVProf.begin(), nVProf.end(), nProf); - - if (it == nVProf.end()) - { - // Not found - nVProf.push_back(nProf); - nVProfsLineSeg.push_back(nProfsLineSeg); - } - - else - { - // Found - int const nPos = static_cast(it - nVProf.begin()); - int nNewProfsLineSeg = nVProfsLineSeg[nPos]; - nNewProfsLineSeg++; - - nVProfsLineSeg[nPos] = nNewProfsLineSeg; - pThisProfile->SetProfsLineSeg(nSeg, nCoinc, nNewProfsLineSeg); - } - } - } - - // assert(pThisProfile->nGetProfileSize() > 1); - } - - // // DEBUG CODE **************************************************************** - // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred - // vector > prVToTruncateCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); - // int nNumToTruncateCoincidentCHECK2 = prVToTruncateCoincidentProfilesCHECK2.size(); - // for (int nn = 0; nn < nNumToTruncateCoincidentCHECK2; nn++) - // { - // int nThisProfile = prVToTruncateCoincidentProfilesCHECK2[nn].first; - // LogStream << "\tAFTER TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ("; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) - // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; - // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are "; - // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) - // { - // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); - // LogStream << "{ "; - // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) - // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; - // LogStream << "} "; - // } - // LogStream << endl; - // - // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) - // { - // CGeom2DPoint - // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), - // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); - // - // if (Pt1 == Pt2) - // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; - // } - // } - // // DEBUG CODE ****************************************************************** -} - +/*! + \file create_profiles.cpp + \brief Creates profiles which are approximately normal to the coastline, these will become inter-polygon boundaries + \details TODO 001 A more detailed description of these routines. + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* ============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +==============================================================================================================================*/ +#include + +#include +#include + +#include +using std::cerr; +using std::endl; +using std::ios; + +#include +using std::find; +using std::sort; + +#include +using std::make_pair; +using std::pair; + +#include +using std::normal_distribution; + +#include "cme.h" +#include "simulation.h" +#include "coast.h" +#include "2d_point.h" +#include "2di_point.h" + +namespace +{ +//=============================================================================================================================== +//! Function used to sort coastline curvature values when locating start points of normal profiles +//=============================================================================================================================== +bool bCurvaturePairCompareDescending(const pair& prLeft, const pair& prRight) +{ + // Sort in descending order (i.e. most concave first) + return prLeft.second > prRight.second; +} +} // namespace + +//=============================================================================================================================== +//! Create coastline-normal profiles for all coastlines. The first profiles are created 'around' the most concave bits of coast. Also create 'special' profiles at the start and end of the coast, and put these onto the raster grid +//=============================================================================================================================== +int CSimulation::nCreateAllProfiles(void) +{ + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << endl << m_ulIter << ": Creating profiles" << endl; + + for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++) + { + int nProfile = 0; + int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); + + // Create a bool vector to mark coast points which have been searched + vector bVCoastPointDone(nCoastSize, false); + + // Now create a vector of pairs: the first value of the pair is the coastline point, the second is the coastline's curvature at that point + vector> prVCurvature; + + for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) + { + double dCurvature; + + int const nCat = m_VCoast[nCoast].pGetCoastLandform(nCoastPoint)->nGetLandFormCategory(); + if ((nCat != LF_INTERVENTION_STRUCT) && (nCat != LF_INTERVENTION_NON_STRUCT)) + { + // Not an intervention coast point, so store the smoothed curvature + dCurvature = m_VCoast[nCoast].dGetSmoothCurvature(nCoastPoint); + } + else + { + // This is an intervention coast point, which is likely to have some sharp angles. So store the detailed curvature + dCurvature = m_VCoast[nCoast].dGetDetailedCurvature(nCoastPoint); + } + + prVCurvature.push_back(make_pair(nCoastPoint, dCurvature)); + } + + // Sort this pair vector in descending order, so that the most convex curvature points are first + sort(prVCurvature.begin(), prVCurvature.end(), bCurvaturePairCompareDescending); + + // // DEBUG CODE ======================================================================================================================= + // for (int n = 0; n < prVCurvature.size(); n++) + // { + // LogStream << prVCurvature[n].first << "\t" << prVCurvature[n].second << endl; + // } + // LogStream << endl << endl; + // // DEBUG CODE ======================================================================================================================= + + // And mark points at and near the start and end of the coastline so that they don't get searched (will be creating 'special' start- and end-of-coast profiles at these end points later) + for (int n = 0; n < m_nCoastNormalSpacing; n++) + { + if (n < nCoastSize) + bVCoastPointDone[n] = true; + + int const m = nCoastSize - n - 1; + + if (m >= 0) + bVCoastPointDone[m] = true; + } + + // Now locate the start points for all coastline-normal profiles (except the grid-edge ones), at points of maximum convexity. Then create the profiles + LocateAndCreateProfiles(nCoast, nProfile, &bVCoastPointDone, &prVCurvature); + + // Did we fail to create any normal profiles? If so, quit + if (nProfile < 0) + { + string strErr = ERR + "timestep " + strDblToStr(m_ulIter) + ": could not create profiles for coastline " + strDblToStr(nCoast); + + if (m_ulIter == 1) + strErr += ". Check the SWL"; + + strErr += "\n"; + + cerr << strErr; + LogStream << strErr; + + return RTN_ERR_NO_PROFILES_1; + } + + // Locate and create a 'special' profile at the grid edge, first at the beginning of the coastline. Then put this onto the raster grid + int nRet = nLocateAndCreateGridEdgeProfile(true, nCoast, nProfile); + + if (nRet != RTN_OK) + return nRet; + + // Locate a second 'special' profile at the grid edge, this time at end of the coastline. Then put this onto the raster grid + nRet = nLocateAndCreateGridEdgeProfile(false, nCoast, ++nProfile); + + if (nRet != RTN_OK) + return nRet; + + // Insert pointers to profiles at coastline points in the profile-all-coastpoint index + m_VCoast[nCoast].InsertProfilesInProfileCoastPointIndex(); + + // // DEBUG CODE =================================================================================================== + // LogStream << endl << "===========================================================================================" << endl; + // LogStream << "PROFILES BEFORE ADDING BEFORE- AND AFTER-PROFILE NUMBERS" << endl; + // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles(); + // for (int nn = 0; nn < nNumProfiles; nn++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn); + // + // LogStream << nn << " nCoastID = " << pProfile->nGetProfileID() << " nGlobalID = " << pProfile->nGetProfileID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; + // } + // LogStream << "===================================================================================================" << endl << endl; + // // DEBUG CODE =================================================================================================== + + CGeomProfile* pLastProfile; + CGeomProfile* pThisProfile; + + // Go along the coastline and give each profile the number of the adjacent up-coast profile and the adjacent down-coast profile + for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) + { + if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint)) + { + pThisProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint); + // nThisProfile = pThisProfile->nGetProfileID(); + + if (nCoastPoint == 0) + { + pThisProfile->SetUpCoastAdjacentProfile(NULL); + + // LogStream << "nCoastPoint = " << nCoastPoint << " ThisProfile = " << nThisProfile << " ThisProfile UpCoast = " << pThisProfile->pGetUpCoastAdjacentProfile() << " ThisProfile DownCoast = " << pThisProfile->pGetDownCoastAdjacentProfile() << endl; + + pLastProfile = pThisProfile; + // nLastProfile = nThisProfile; + continue; + } + + pLastProfile->SetDownCoastAdjacentProfile(pThisProfile); + pThisProfile->SetUpCoastAdjacentProfile(pLastProfile); + + if (nCoastPoint == nCoastSize - 1) + pThisProfile->SetDownCoastAdjacentProfile(NULL); + + pLastProfile = pThisProfile; + } + } + + // And create an index to this coast's profiles in along-coastline sequence + m_VCoast[nCoast].CreateProfileDownCoastIndex(); + + // // DEBUG CODE ======================================================================================================================= + // for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); + // CGeomProfile* pUpCoastProfile = pProfile->pGetUpCoastAdjacentProfile(); + // CGeomProfile* pDownCoastProfile = pProfile->pGetDownCoastAdjacentProfile(); + // int nUpCoastProfile = INT_NODATA; + // int nDownCoastProfile = INT_NODATA; + // if (pUpCoastProfile != 0) + // nUpCoastProfile = pUpCoastProfile->nGetProfileID(); + // if (pDownCoastProfile != 0) + // nDownCoastProfile = pDownCoastProfile->nGetProfileID(); + // LogStream << "nCoastID = " << pProfile->nGetProfileID() << "\t up-coast profile = " << nUpCoastProfile << "\t down-coast profile = " << nDownCoastProfile << endl; + // } + // LogStream << endl; + // // DEBUG CODE ======================================================================================================================= + + // // DEBUG CODE ======================================================================================================================= + // int nProf = 0; + // for (int n = 0; n < nCoastSize; n++) + // { + // // LogStream << n << "\t"; + // + // // LogStream << m_VCoast[nCoast].dGetDetailedCurvature(n) << "\t"; + // // + // // LogStream << m_VCoast[nCoast].dGetSmoothCurvature(n) << "\t"; + // // + // // if (m_VCoast[nCoast].pGetCoastLandform(n)->nGetLandFormCategory() == LF_INTERVENTION) + // // LogStream << "I\t"; + // + // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(n)) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(n); + // + // LogStream << "profile " << pProfile->nGetProfileID() << " at coast point " << n << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; + // + // nProf++; + // } + // } + // LogStream << endl; + // LogStream << "nProf = " << nProf << endl; + // // DEBUG CODE ======================================================================================================================= + + // // DEBUG CODE ======================================================================================================================= + // LogStream << "=====================" << endl; + // for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); + // int nStartPoint = pProfile->nGetCoastPoint(); + // + // LogStream << n << "\t nCoastID = " << pProfile->nGetProfileID() << "\tnStartPoint = " << nStartPoint << endl; + // } + // LogStream << endl; + // LogStream << "=====================" << endl; + // // DEBUG CODE ======================================================================================================================= + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! For a single coastline, locate the start points for all coastline-normal profiles (except the grid-edge profiles). Then create the profiles +//=============================================================================================================================== +void CSimulation::LocateAndCreateProfiles(int const nCoast, int& nProfile, vector* pbVCoastPointDone, vector> const* prVCurvature) +{ + int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); + + // Work along the vector of curvature pairs starting at the convex end + for (int n = nCoastSize - 1; n >= 0; + n--) + { + // Have we searched all the coastline points? + int nStillToSearch = 0; + + for (int m = 0; m < nCoastSize; m++) + if (! pbVCoastPointDone->at(m)) + nStillToSearch++; + + if (nStillToSearch == 0) + // OK we are done here + return; + + // This convex point on the coastline is a potential location for a normal + int const nNormalPoint = prVCurvature->at(n).first; + + // Ignore each end of the coastline + if ((nNormalPoint == 0) || (nNormalPoint == nCoastSize - 1)) + continue; + + // TODO 089 When choosing locations for profiles, do coast first then interventions + + if (! pbVCoastPointDone->at(nNormalPoint)) + { + // We have not already searched this coast point. Is it an intervention coast point? + bool bIntervention = false; + + int const nCat = m_VCoast[nCoast].pGetCoastLandform(nNormalPoint)->nGetLandFormCategory(); + if ((nCat == LF_INTERVENTION_STRUCT) || (nCat == LF_INTERVENTION_NON_STRUCT)) + { + // It is an intervention + bIntervention = true; + } + + CGeom2DIPoint const PtiThis = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nNormalPoint); + + // Create a profile here + int const nRet = nCreateProfile(nCoast, nCoastSize, nNormalPoint, nProfile, bIntervention, &PtiThis); + + // // DEBUG CODE ================= + // LogStream << "After nCreateProfile() ===========" << endl; + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); + // LogStream << pProfile->nGetProfileID() << "\t"; + // + // int nPointsInProfile = pProfile->nGetProfileSize(); + // + // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++) + // { + // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint); + // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}"; + // } + // LogStream << endl << "===========" << endl; + // // DEBUG CODE ================= + + // Mark this coast point as searched + pbVCoastPointDone->at(nNormalPoint) = true; + + if (nRet != RTN_OK) + { + // This potential profile is no good (has hit coast, or hit dry land, etc.) so forget about it + // LogStream << "Profile is no good" << endl; + continue; + } + + // // DEBUG CODE =================================================================================================== + // LogStream << endl << "===========================================================================================" << endl; + // LogStream << "PROFILES JUST AFTER CREATION" << endl; + // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles(); + // for (int nn = 0; nn < nNumProfiles; nn++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn); + // + // LogStream << nn << " nCoastID = " << pProfile->nGetProfileID() << " nGlobalID = " << pProfile->nGetProfileID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; + // } + // LogStream << "===================================================================================================" << endl << endl; + // // DEBUG CODE =================================================================================================== + // + // // DEBUG CODE =================================================================================================== + // LogStream << "++++++++++++++++++++++" << endl; + // LogStream << endl << "Just created profile " << nProfile << endl; + // int nProf = 0; + // for (int nnn = 0; nnn < nCoastSize; nnn++) + // { + // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nnn)) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nnn); + // + // LogStream << "profile " << pProfile->nGetProfileID() << " at coast point " << nnn << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl; + // + // nProf++; + // } + // } + // LogStream << endl; + // LogStream << "nProf = " << nProf << endl; + // LogStream << "++++++++++++++++++++++" << endl; + // // DEBUG CODE =================================================================================================== + + // CGeom2DPoint PtThis = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nNormalPoint); + // if (m_nLogFileDetail >= LOG_FILE_ALL) + // LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " created at coast point " << nNormalPoint << " [" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] = {" << PtThis.dGetX() << ", " << PtThis.dGetY() << "} (smoothed curvature = " << m_VCoast[nCoast].dGetSmoothCurvature(nNormalPoint) << ", detailed curvature = " << m_VCoast[nCoast].dGetDetailedCurvature(nNormalPoint) << ")" << endl; + + // // DEBUG CODE ================================================================================= + // if (m_pRasterGrid->m_Cell[PtiThis.nGetX()][PtiThis.nGetY()].bIsCoastline()) + // LogStream << m_ulIter << ": cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS coastline, coast number = " << m_pRasterGrid->m_Cell[PtiThis.nGetX()][PtiThis.nGetY()].nGetCoastline() << endl; + // else + // LogStream << m_ulIter << ": ******* cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS NOT coastline" << endl; + // // DEBUG CODE ================================================================================= + + // This profile is fine + nProfile++; + + // We need to mark points on either side of this profile so that we don't get profiles which are too close together. However best-placed profiles on narrow intervention structures may need to be quite closes + double dNumToMark = m_nCoastNormalSpacing; + + if (bIntervention) + dNumToMark = m_nCoastNormalInterventionSpacing; + + // If we have a random factor for profile spacing, then modify the profile spacing + if (m_dCoastNormalRandSpacingFactor > 0) + { + // Draw a sample from the unit normal distribution using random number generator 0 + double const dRand = m_dGetFromUnitNormalDist(m_Rand[0]); + + double const dTmp = dRand * m_dCoastNormalRandSpacingFactor * dNumToMark; + dNumToMark += dTmp; + + // Make sure number to mark is not too small or too big TODO 011 + if (bIntervention) + { + dNumToMark = tMin(dNumToMark, m_nCoastNormalInterventionSpacing * 0.75); + dNumToMark = tMax(dNumToMark, m_nCoastNormalInterventionSpacing * 1.25); + } + + else + { + dNumToMark = tMin(dNumToMark, m_nCoastNormalSpacing * 0.75); + dNumToMark = tMax(dNumToMark, m_nCoastNormalSpacing * 1.25); + } + + // TODO 014 Assume that the above is the profile spacing on straight bits of coast. Try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity. Could use a Michaelis-Menten S-curve relationship for this i.e. + // double fReN = pow(NowCell[nX][nY].dGetReynolds(m_dNu), m_dDepN); + // double fC1 = m_dC1Laminar - ((m_dC1Diff * fReN) / (fReN + m_dReMidN)); + } + + // Mark points on either side of the profile + for (int m = 1; m < dNumToMark; m++) + { + int nTmpPoint = nNormalPoint + m; + + if (nTmpPoint < nCoastSize) + pbVCoastPointDone->at(nTmpPoint) = true; + + nTmpPoint = nNormalPoint - m; + + if (nTmpPoint >= 0) + pbVCoastPointDone->at(nTmpPoint) = true; + } + } + } +} + +//=============================================================================================================================== +//! Creates a single coastline-normal profile (which may be an intervention profile) +//=============================================================================================================================== +int CSimulation::nCreateProfile(int const nCoast, int const nCoastSize, int const nProfileStartPoint, int const nProfile, bool const bIntervention, CGeom2DIPoint const* pPtiStart) +{ + // OK, we have flagged the start point of this new coastline-normal profile, so create it. Make the start of the profile the centroid of the actual cell that is marked as coast (not the cell under the smoothed vector coast, they may well be different) + CGeom2DPoint PtStart; // In external CRS + PtStart.SetX(dGridCentroidXToExtCRSX(pPtiStart->nGetX())); + PtStart.SetY(dGridCentroidYToExtCRSY(pPtiStart->nGetY())); + + CGeom2DPoint PtEnd; // In external CRS + CGeom2DIPoint PtiEnd; // In grid CRS + int const nRet = nGetCoastNormalEndPoint(nCoast, nProfileStartPoint, nCoastSize, &PtStart, m_dCoastNormalLength, &PtEnd, &PtiEnd, bIntervention); + if (nRet == RTN_ERR_NO_SOLUTION_FOR_ENDPOINT) + { + // Could not solve end-point equation, so forget about this profile + return nRet; + } + + int const nXEnd = PtiEnd.nGetX(); + int const nYEnd = PtiEnd.nGetY(); + + // Safety check: is the end point in the contiguous sea? + if (! m_pRasterGrid->m_Cell[nXEnd][nYEnd].bIsInContiguousSea()) + { + // if (m_nLogFileDetail >= LOG_FILE_ALL) + // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " has inland end point at [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl; + + return RTN_ERR_PROFILE_ENDPOINT_IS_INLAND; + } + + // Safety check: is the water depth at the end point less than the depth of closure? + if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure) + { + // if (m_nLogFileDetail >= LOG_FILE_ALL) + // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl; + + return RTN_ERR_PROFILE_END_INSUFFICIENT_DEPTH; + } + + // No problems, so create the new profile + CGeomProfile* pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, bIntervention); + + // And create the profile's coastline-normal vector. Only two points (start and end points, both external CRS) are stored + vector VNormal; + VNormal.push_back(PtStart); + VNormal.push_back(PtEnd); + + // Set the start and end points (external CRS) of the profile + pProfile->SetPointsInProfile(&VNormal); + + // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment + pProfile->AppendLineSegment(); + pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0)); + + // Save the profile, note that several fields in the profile are still blank + m_VCoast[nCoast].AppendProfile(pProfile); + + // // DEBUG CODE ================= + // LogStream << "in nCreateProfile() ===========" << endl; + // // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); + // LogStream << pProfile->nGetProfileID() << "\t"; + // + // int nPointsInProfile = pProfile->nGetProfileSize(); + // + // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++) + // { + // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint); + // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}"; + // } + // LogStream << endl << "===========" << endl; + // // DEBUG CODE ================= + + // assert(pProfile->nGetProfileSize() > 0); + + LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " created at coast point " << nProfileStartPoint << " from [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] = {" << PtStart.dGetX() << ", " << PtStart.dGetY() << "} to [" << PtiEnd.nGetX() << "][" << PtiEnd.nGetY() << "] = {" << PtEnd.dGetX() << ", " << PtEnd.dGetY() << "}" << (pProfile->bIsIntervention() ? ", from intervention" : "") << endl; + + return RTN_OK; +} + +//=============================================================================================================================== +//! Creates a 'special' profile at each end of a coastline, at the edge of the raster grid. This profile is not necessarily normal to the coastline since it goes along the grid's edge +//=============================================================================================================================== +int CSimulation::nLocateAndCreateGridEdgeProfile(bool const bCoastStart, int const nCoast, int& nProfile) +{ + int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); + int const nHandedness = m_VCoast[nCoast].nGetSeaHandedness(); + int const nProfileLen = nRound(m_dCoastNormalLength / m_dCellSide); // Profile length in grid CRS + int nProfileStartEdge; + + CGeom2DIPoint PtiProfileStart; // In grid CRS + vector VPtiNormalPoints; // In grid CRS + + if (bCoastStart) + { + // At start of coast + PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0); // Grid CRS + nProfileStartEdge = m_VCoast[nCoast].nGetStartEdge(); + } + else + { + // At end of coast + PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1); // Grid CRS + nProfileStartEdge = m_VCoast[nCoast].nGetEndEdge(); + } + + VPtiNormalPoints.push_back(PtiProfileStart); + + // Find the start cell in the list of edge cells + auto it = find(m_VEdgeCell.begin(), m_VEdgeCell.end(), PtiProfileStart); + + if (it == m_VEdgeCell.end()) + { + // Not found. This can happen because of rounding problems, i.e. the cell which was stored as the first cell of the raster coastline + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter << ": " << ERR << " when constructing start-of-coast profile, [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} not found in list of edge cells" << endl; + + return RTN_ERR_COAST_CANT_FIND_EDGE_CELL; + } + + // Found + int nPos = static_cast(it - m_VEdgeCell.begin()); + + // Now construct the edge profile, searching for edge cells + for (int n = 0; n < nProfileLen; n++) + { + if (bCoastStart) + { + // At start of coast + if (nHandedness == LEFT_HANDED) + { + // The list of edge cells is in clockwise sequence, go in this direction + nPos++; + + if (nPos >= static_cast(m_VEdgeCell.size())) + { + // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this + break; + } + } + else // Right-handed + { + // The list of edge cells is in clockwise sequence, go in the opposite direction + nPos--; + + if (nPos < 0) + { + // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this + break; + } + } + } + else + { + // At end of coast + if (nHandedness == LEFT_HANDED) + { + // The list of edge cells is in clockwise sequence, go in the opposite direction + nPos--; + + if (nPos < 0) + { + // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this + break; + } + } + else // Right-handed + { + // The list of edge cells is in clockwise sequence, go in this direction + nPos++; + + if (nPos >= static_cast(m_VEdgeCell.size())) + { + // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this + break; + } + } + } + + if (m_VEdgeCellEdge[nPos] != nProfileStartEdge) + { + // We've reached the end of a grid side before the profile is long enough. OK, we can live with this + break; + } + + // All OK, so append this grid-edge cell, making sure that there is no gap between this and the previously-appended cell (if there is, will get problems with cell-by-cell fill) + AppendEnsureNoGap(&VPtiNormalPoints, &m_VEdgeCell[nPos]); + } + + int nProfileStartPoint; + CGeomProfile* pProfile; + CGeom2DIPoint const PtiDummy(INT_NODATA, INT_NODATA); + + if (bCoastStart) + { + nProfileStartPoint = 0; + + // Create the new start-of-coast profile + pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, false); + + // Mark this as a start-of-coast profile + pProfile->SetStartOfCoast(true); + } + else + { + nProfileStartPoint = nCoastSize - 1; + + // Create the new end-of-coast profile + pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, false); + + // Mark this as an end-of-coast profile + pProfile->SetEndOfCoast(true); + } + + // Create the list of cells 'under' this grid-edge profile. Note that more than two cells are stored + for (unsigned int n = 0; n < VPtiNormalPoints.size(); n++) + { + int const nX = VPtiNormalPoints[n].nGetX(); + int const nY = VPtiNormalPoints[n].nGetY(); + + // Mark each cell in the raster grid + m_pRasterGrid->m_Cell[nX][nY].SetCoastAndProfileID(nCoast, nProfile); + + // Store the raster grid coordinates in the profile object + pProfile->AppendCellInProfile(nX, nY); + + CGeom2DPoint const Pt(dGridCentroidXToExtCRSX(nX), dGridCentroidYToExtCRSY(nY)); // In external CRS + + // Store the external coordinates in the profile object. Note that for this grid-edge profile, the coordinates of the cells and the coordinates of points on the profile itself are identical, this is not the case for ordinary profiles + pProfile->AppendPointInProfile(&Pt); + } + + int const nEndX = VPtiNormalPoints.back().nGetX(); + int const nEndY = VPtiNormalPoints.back().nGetY(); + + // Get the deep water wave height and orientation values at the end of the profile + double const dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveHeight(); + double const dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveAngle(); + double const dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWavePeriod(); + + // And store them in this profile + pProfile->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight); + pProfile->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle); + pProfile->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod); + + // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment + pProfile->AppendLineSegment(); + pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0)); + + // Store the grid-edge profile + m_VCoast[nCoast].AppendProfile(pProfile); + m_VCoast[nCoast].SetProfileAtCoastPoint(nProfileStartPoint, pProfile); + + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": \tcoast " << nCoast << " grid-edge profile " << nProfile << " created at coast " << (bCoastStart ? "start" : "end") << " point " << (bCoastStart ? 0 : nCoastSize - 1) << ", from [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} to [" << VPtiNormalPoints.back().nGetX() << "][" << VPtiNormalPoints.back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(VPtiNormalPoints.back().nGetX()) << ", " << dGridCentroidYToExtCRSY(VPtiNormalPoints.back().nGetY()) << "}" << endl; + + // assert(pProfile->nGetProfileSize() > 0); + + return RTN_OK; +} + +//=============================================================================================================================== +//! Finds the end point of a coastline-normal line, given the start point on the vector coastline. If however the start point is on the grid edge (only applicable to cliff collapse [rofiles), then the end point is also on the grid edge, and the line joining the start and end points is not necessarily normal to the vector coast. All input coordinates are in the external CRS +//=============================================================================================================================== +int CSimulation::nGetCoastNormalEndPoint(int const nCoast, int const nStartCoastPoint, int const nCoastSize, CGeom2DPoint const* pPtStart, double const dLineLength, CGeom2DPoint* pPtEnd, CGeom2DIPoint* pPtiEnd, bool const bIntervention) +{ + int const AVGSIZE = 21; // TODO 011 This should be a user input + + double dXEnd1 = 0; + double dXEnd2 = 0; + double dYEnd1 = 0; + double dYEnd2 = 0; + + CGeom2DPoint PtBefore; + CGeom2DPoint PtAfter; + + if (bIntervention) + { + // This is an intervention profile, so just use one point on either side (coordinates in external CRS). TODO Note this this assumes that this intervention profile is not at the start or end of the coastline + PtBefore = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint - 1); + PtAfter = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint + 1); + } + else + { + // This is not an intervention profile. It could be a cliff collapse profile, which could be a grid-edge profile + double const dXStart = pPtStart->dGetX(); + double const dYStart = pPtStart->dGetY(); + + int const nXStart = nRound(dExtCRSXToGridX(dXStart)); + int const nYStart = nRound(dExtCRSYToGridY(dYStart)); + int const nLineLength = nConvertMetresToNumCells(dLineLength); + + // LogStream << nXStart << ", " << nYStart << endl; + if ((nXStart == 0) || (nXStart == m_nXGridSize-1)) + { + // Yes it is a grid-edge profile + dXEnd1 = dGridXToExtCRSX(nXStart); + dXEnd2 = dXEnd1; + + dYEnd1 = dGridYToExtCRSY(nYStart + nLineLength); + dYEnd2 = dGridYToExtCRSY(nYStart - nLineLength); + } + else if ((nYStart == 0) || (nYStart == m_nYGridSize-1)) + { + // Yes it is a grid-edge profile + dYEnd1 = dGridYToExtCRSY(nYStart); + dYEnd2 = dYEnd1; + + dXEnd1 = dGridXToExtCRSX(nXStart + nLineLength); + dXEnd2 = dGridXToExtCRSX(nXStart - nLineLength); + } + else + { + // This is not a grid-edge profile, so put a maximum of AVGSIZE points before the start point into a vector + vector VPtBeforeToAverage; + + for (int n = 1; n <= AVGSIZE; n++) + { + int const nPoint = nStartCoastPoint - n; + if (nPoint < 0) + break; + + VPtBeforeToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint)); + } + + // Put a maximum of AVGSIZE points after the start point into a vector + vector VPtAfterToAverage; + + for (int n = 1; n <= AVGSIZE; n++) + { + int const nPoint = nStartCoastPoint + n; + if (nPoint > nCoastSize - 1) + break; + + VPtAfterToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint)); + } + + // Now average each of these vectors of points: results are in PtBefore and PtAfter (coordinates in external CRS) + PtBefore = PtAverage(&VPtBeforeToAverage); + PtAfter = PtAverage(&VPtAfterToAverage); + + // Get the y = a * x + b equation of the straight line linking the coastline points before and after 'this' coastline point. For this linking line, slope a = (y2 - y1) / (x2 - x1) + double const dYDiff = PtAfter.dGetY() - PtBefore.dGetY(); + double const dXDiff = PtAfter.dGetX() - PtBefore.dGetX(); + + if (bFPIsEqual(dYDiff, 0.0, TOLERANCE)) + { + // The linking line runs W-E or E-W, so a straight line at right angles to this runs N-S or S-N. Calculate the two possible end points for this coastline-normal profile + dXEnd1 = dXEnd2 = pPtStart->dGetX(); + dYEnd1 = pPtStart->dGetY() + dLineLength; + dYEnd2 = pPtStart->dGetY() - dLineLength; + } + else if (bFPIsEqual(dXDiff, 0.0, TOLERANCE)) + { + // The linking line runs N-S or S-N, so a straight line at right angles to this runs W-E or E-W. Calculate the two possible end points for this coastline-normal profile + dYEnd1 = dYEnd2 = pPtStart->dGetY(); + dXEnd1 = pPtStart->dGetX() + dLineLength; + dXEnd2 = pPtStart->dGetX() - dLineLength; + } + else + { + // The linking line runs neither W-E nor N-S so we have to work a bit harder to find the end-point of the coastline-normal profile + double const dA = dYDiff / dXDiff; + + // Now calculate the equation of the straight line which is perpendicular to this linking line + double const dAPerp = -1 / dA; + double const dBPerp = pPtStart->dGetY() - (dAPerp * pPtStart->dGetX()); + + // Calculate the end point of the profile: first do some substitution then rearrange as a quadratic equation i.e. in the form Ax^2 + Bx + C = 0 (see http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle) + double const dQuadA = 1 + (dAPerp * dAPerp); + double const dQuadB = 2 * ((dBPerp * dAPerp) - (dAPerp * pPtStart->dGetY()) - pPtStart->dGetX()); + double const dQuadC = ((pPtStart->dGetX() * pPtStart->dGetX()) + (pPtStart->dGetY() * pPtStart->dGetY()) + (dBPerp * dBPerp) - (2 * pPtStart->dGetY() * dBPerp) - (dLineLength * dLineLength)); + + // Solve for x and y using the quadratic formula x = (−B ± sqrt(B^2 − 4AC)) / 2A + double const dDiscriminant = (dQuadB * dQuadB) - (4 * dQuadA * dQuadC); + + if (dDiscriminant < 0) + { + LogStream << ERR << "timestep " << m_ulIter << ": discriminant < 0 when finding profile end point on coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl; + return RTN_ERR_NO_SOLUTION_FOR_ENDPOINT; + } + + dXEnd1 = (-dQuadB + sqrt(dDiscriminant)) / (2 * dQuadA); + dYEnd1 = (dAPerp * dXEnd1) + dBPerp; + dXEnd2 = (-dQuadB - sqrt(dDiscriminant)) / (2 * dQuadA); + dYEnd2 = (dAPerp * dXEnd2) + dBPerp; + } + } + } + + // We have two possible solutions, so decide which of the two endpoints to use then create the profile end-point (coordinates in external CRS) + int const nSeaHand = m_VCoast[nCoast].nGetSeaHandedness(); // Assumes handedness is either 0 or 1 (i.e. not -1) + *pPtEnd = PtChooseEndPoint(nSeaHand, &PtBefore, &PtAfter, dXEnd1, dYEnd1, dXEnd2, dYEnd2); + + // Check that pPtiEnd is not off the grid. Note that pPtiEnd is not necessarily a cell centroid + pPtiEnd->SetXY(nRound(dExtCRSXToGridX(pPtEnd->dGetX())), nRound(dExtCRSYToGridY(pPtEnd->dGetY()))); + + if (! bIsWithinValidGrid(pPtiEnd)) + { + // LogStream << m_ulIter << ": profile endpoint is outside grid [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}. The profile starts at coastline point " << nStartCoastPoint << " = {" << pPtStart->dGetX() << ", " << pPtStart->dGetY() << "}" << endl; + + // The end point is off the grid, so constrain it to be within the valid grid + CGeom2DIPoint const PtiStart(nRound(dExtCRSXToGridX(pPtStart->dGetX())), nRound(dExtCRSYToGridY(pPtStart->dGetY()))); + KeepWithinValidGrid(&PtiStart, pPtiEnd); + + pPtEnd->SetX(dGridCentroidXToExtCRSX(pPtiEnd->nGetX())); + pPtEnd->SetY(dGridCentroidYToExtCRSY(pPtiEnd->nGetY())); + + // LogStream << m_ulIter << ": \tcoast " << nCoast << " profile endpoint constrained to be within grid, is now [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}. The profile starts at coastline point " << nStartCoastPoint << " = {" << pPtStart->dGetX() << ", " << pPtStart->dGetY() << "}" << endl; + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! Choose which end point to use for the coastline-normal profile +//=============================================================================================================================== +CGeom2DPoint CSimulation::PtChooseEndPoint(int const nHand, CGeom2DPoint const* PtBefore, CGeom2DPoint const* PtAfter, double const dXEnd1, double const dYEnd1, double const dXEnd2, double const dYEnd2) +{ + CGeom2DPoint PtChosen; + + // All coordinates here are in the external CRS, so the origin of the grid is the bottom left + if (nHand == RIGHT_HANDED) + { + // The sea is to the right of the linking line. So which way is the linking line oriented? First check the N-S component + if (PtAfter->dGetY() > PtBefore->dGetY()) + { + // We are going S to N and the sea is to the right: the normal endpoint is to the E. We want the larger of the two x values + if (dXEnd1 > dXEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else if (PtAfter->dGetY() < PtBefore->dGetY()) + { + // We are going N to S and the sea is to the right: the normal endpoint is to the W. We want the smaller of the two x values + if (dXEnd1 < dXEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else + { + // No N-S component i.e. the linking line is exactly W-E. So check the W-E component + if (PtAfter->dGetX() > PtBefore->dGetX()) + { + // We are going W to E and the sea is to the right: the normal endpoint is to the s. We want the smaller of the two y values + if (dYEnd1 < dYEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident + { + // We are going E to W and the sea is to the right: the normal endpoint is to the N. We want the larger of the two y values + if (dYEnd1 > dYEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + } + } + else // nHand == LEFT_HANDED + { + // The sea is to the left of the linking line. So which way is the linking line oriented? First check the N-S component + if (PtAfter->dGetY() > PtBefore->dGetY()) + { + // We are going S to N and the sea is to the left: the normal endpoint is to the W. We want the smaller of the two x values + if (dXEnd1 < dXEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else if (PtAfter->dGetY() < PtBefore->dGetY()) + { + // We are going N to S and the sea is to the left: the normal endpoint is to the E. We want the larger of the two x values + if (dXEnd1 > dXEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else + { + // No N-S component i.e. the linking line is exactly W-E. So check the W-E component + if (PtAfter->dGetX() > PtBefore->dGetX()) + { + // We are going W to E and the sea is to the left: the normal endpoint is to the N. We want the larger of the two y values + if (dYEnd1 > dYEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident + { + // We are going E to W and the sea is to the left: the normal endpoint is to the S. We want the smaller of the two y values + if (dYEnd1 < dYEnd2) + { + PtChosen.SetX(dXEnd1); + PtChosen.SetY(dYEnd1); + } + else + { + PtChosen.SetX(dXEnd2); + PtChosen.SetY(dYEnd2); + } + } + } + } + + return PtChosen; +} + +//=============================================================================================================================== +//! Checks all coastline-normal profiles for intersection, and modifies those that intersect +//=============================================================================================================================== +void CSimulation::CheckForIntersectingProfiles(void) +{ + LogStream << endl << m_ulIter << ": Checking for profile intersection" << endl; + + // Do once for every coastline object + int const nCoastLines = static_cast(m_VCoast.size()); + + for (int nCoast = 0; nCoast < nCoastLines; nCoast++) + { + int const nCoastSize = m_VCoast[nCoast].nGetCoastlineSize(); + + // Do once for every profile, in along-coast sequence + for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++) + { + if (! m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint)) + continue; + + // There is a profile at this coast point + CGeomProfile* pFirstProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint); + int const nFirstProfile = pFirstProfile->nGetProfileID(); + + // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however + if (! pFirstProfile->bProfileOKIncTruncated()) + { + // LogStream << m_ulIter << ": nCoastPoint = " << nCoastPoint << " pFirstProfile = " << pFirstProfile->nGetProfileID() << " is not OK (could be a start- or end-of-coast profile), abandoning" << endl; + continue; + } + + // OK we have found a first profile. Now go along the coast in alternate directions: first down-coast (in the direction of increasing coast point numbers) then up-coast + for (int nDirection = DIRECTION_DOWNCOAST; nDirection <= DIRECTION_UPCOAST; nDirection++) + { + int nStartPoint; + + if (nDirection == DIRECTION_DOWNCOAST) + nStartPoint = nCoastPoint + 1; + else + nStartPoint = nCoastPoint - 1; + + for (int nSecondCoastPoint = nStartPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint < nCoastSize : nSecondCoastPoint >= 0; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint++ : nSecondCoastPoint--) + // // In this direction, look at profiles which are increasingly close to the first profile + // int nStartPoint; + // if (nDirection == DIRECTION_DOWNCOAST) + // nStartPoint = 0; + // else + // nStartPoint = nCoastSize - 1; + // + // for (int nSecondCoastPoint = nStartPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint < nCoastPoint : nSecondCoastPoint > nCoastPoint; (nDirection == DIRECTION_DOWNCOAST) ? nSecondCoastPoint++ : nSecondCoastPoint--) + { + if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nSecondCoastPoint)) + { + // There is a profile at the second coast point, so get a pointer to it + CGeomProfile* pSecondProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nSecondCoastPoint); + int const nSecondProfile = pSecondProfile->nGetProfileID(); + + // LogStream << m_ulIter << ": " << (nDirection == DIRECTION_DOWNCOAST ? "down" : "up") << "-coast search, nCoastPoint = " << nCoastPoint << " nSecondCoastPoint = " << nSecondCoastPoint << " (profiles " << pFirstProfile->nGetProfileID() << " and " << pSecondProfile->nGetProfileID() << ")" << endl; + + // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however + if (! pSecondProfile->bProfileOKIncTruncated()) + { + // LogStream << m_ulIter << ": second profile = " << pSecondProfile->nGetProfileID() << " is not OK (could be a start- or end-of-coast profile), abandoning" << endl; + continue; + } + + // Only check these two profiles for intersection if they are are not co-incident in the final line segment of both profiles (i.e. the profiles have not already intersected) + if ((pFirstProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nSecondProfile)) || (pSecondProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nFirstProfile))) + { + // LogStream << m_ulIter << ": profiles " << pFirstProfile->nGetProfileID() << " and " << pSecondProfile->nGetProfileID() << " are are not co-incident in the final line segment of both profiles (i.e. the profiles have not already intersected), abandoning" << endl; + continue; + } + + // OK go for it + int nProf1LineSeg = 0; + int nProf2LineSeg = 0; + double dIntersectX = 0; + double dIntersectY = 0; + double dAvgEndX = 0; + double dAvgEndY = 0; + + if (bCheckForIntersection(pFirstProfile, pSecondProfile, nProf1LineSeg, nProf2LineSeg, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY)) + { + // The profiles intersect. Decide which profile to truncate, and which to retain + int nPoint = -1; + + if (pFirstProfile->bIsIntervention()) + { + LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, truncate " << nFirstProfile << " since it is an intervention profile" << endl; + + // Truncate the first profile, since it is an intervention profile + TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); + } + else if (pSecondProfile->bIsIntervention()) + { + LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, truncate " << nSecondProfile << " since it is an intervention profile" << endl; + + // Truncate the second profile, since it is an intervention profile + TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); + } + // Is the point of intersection already present in the first profile (i.e. because there has already been an intersection at this point between the first profile and some other profile)? + else if (pFirstProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint)) + { + LogStream << m_ulIter << ": \t coast " << nCoast << " profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nFirstProfile << " as point " << nPoint << endl; + + // Truncate the second profile and merge it with the first profile + TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, true); + } + // Is the point of intersection already present in the second profile? + else if (pSecondProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint)) + { + LogStream << m_ulIter << ": \tcoast " << nCoast << " profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nSecondProfile << " as point " << nPoint << endl; + + // Truncate the first profile and merge it with the second profile + TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, true); + } + else + { + // The point of intersection is not already present in either profile, so get the number of line segments of each profile + int const nFirstProfileLineSegments = pFirstProfile->nGetNumLineSegments(); + int const nSecondProfileLineSegments = pSecondProfile->nGetNumLineSegments(); + + // assert(nProf1LineSeg < nFirstProfileLineSegments); + // assert(nProf2LineSeg < nSecondProfileLineSegments); + + // Next check whether the point of intersection is on the final line segment of both profiles + if ((nProf1LineSeg == (nFirstProfileLineSegments - 1)) && (nProf2LineSeg == (nSecondProfileLineSegments - 1))) + { + // Yes, the point of intersection is on the final line segment of both profiles, so merge the profiles seaward of the point of intersection + MergeProfilesAtFinalLineSegments(nCoast, pFirstProfile, pSecondProfile, nFirstProfileLineSegments, nSecondProfileLineSegments, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY); + + // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, end-segment intersection between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << " segments, and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << " segments, respectively" << endl; + + // // DEBUG CODE ============================================================================================= + // int nSizeTmp = pFirstProfile->nGetProfileSize(); + // CGeom2DPoint PtEndTmp = *pFirstProfile->pPtGetPointInProfile(nSizeTmp-1); + // + // LogStream << m_ulIter << ": end of first profile (" << nFirstProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl; + // + // nSizeTmp = pSecondProfile->nGetProfileSize(); + // PtEndTmp = *pSecondProfile->pPtGetPointInProfile(nSizeTmp-1); + // + // LogStream << m_ulIter << ": end of second profile (" << nSecondProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl; + // // DEBUG CODE ============================================================================================= + } + else + { + // The profiles intersect, but the point of intersection is not on the final line segment of both profiles. One of the profiles will be truncated, the other profile will be retained + // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, intersection (NOT both end segments) between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << ", and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << ", respectively" << endl; + + // Decide which profile to truncate, and which to retain + if (pFirstProfile->bIsIntervention()) + { + // Truncate the first profile, since it is an intervention profile + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pFirstProfile is an intervention profile, so truncate pFirstProfile (" << pFirstProfile->nGetProfileID() << ")" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); + } + else if (pSecondProfile->bIsIntervention()) + { + // Truncate the second profile, since it is an intervention profile + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pSecondProfile is an intervention profile, so truncate pSecondProfile (" << pSecondProfile->nGetProfileID() << ")" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); + } + else if (nFirstProfileLineSegments < nSecondProfileLineSegments) + { + // Truncate the first profile, since it has a smaller number of line segments + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pFirstProfile has a smaller number of line segments, so truncate pFirstProfile (" << pFirstProfile->nGetProfileID() << ")" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); + } + else if (nFirstProfileLineSegments > nSecondProfileLineSegments) + { + // Truncate the second profile, since it has a smaller number of line segments + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", pSecondProfile has a smaller number of line segments, so truncate pSecondProfile (" << pSecondProfile->nGetProfileID() << ")" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); + } + else + { + // Both profiles have the same number of line segments, so choose randomly. Draw a sample from the unit normal distribution using random number generator 1 + double const dRand = m_dGetFromUnitNormalDist(m_Rand[0]); + + if (dRand >= 0.0) + { + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", same number of line segment, randomly truncate pFirstProfile" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false); + } + else + { + // LogStream << m_ulIter << ": pFirstProfile = " << pFirstProfile->nGetProfileID() << " pSecondProfile = " << pSecondProfile->nGetProfileID() << ", same number of line segment, randomly truncate pSecondProfile" << endl; + + TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false); + } + } + } + } + } + } + } + } + } + } +} + +//=============================================================================================================================== +//! Check all coastline-normal profiles and modify the profiles if they intersect, then mark valid profiles on the raster grid +//=============================================================================================================================== +int CSimulation::nCheckAndMarkAllProfiles(void) +{ + // Check to see which coastline-normal profiles intersect. Then modify intersecting profiles so that the sections of each profile seaward of the point of intersection are 'shared' i.e. are multi-lines. This creates the boundaries of the triangular polygons + CheckForIntersectingProfiles(); + + // Again check the normal profiles for insufficient length: is the water depth at the end point less than the depth of closure? We do this again because some profiles may have been shortened as a result of intersection. Do once for every coastline object + for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++) + { + for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++) + { + CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); + int const nProfile = pProfile->nGetProfileID(); + + if (pProfile->bProfileOK()) + { + int const nSize = pProfile->nGetProfileSize(); + + // Safety check + if (nSize == 0) + { + // pProfile->SetTooShort(true); + m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); + LogStream << "Profile " << nProfile << " is too short, size = " << nSize << endl; + continue; + } + + CGeom2DPoint const* pPtEnd = pProfile->pPtGetPointInProfile(nSize - 1); + CGeom2DIPoint const PtiEnd = PtiExtCRSToGridRound(pPtEnd); + int nXEnd = PtiEnd.nGetX(); + int nYEnd = PtiEnd.nGetY(); + + // Safety checks: the point may be outside the grid, so keep it within the grid + nXEnd = tMin(nXEnd, m_nXGridSize - 1); + nYEnd = tMin(nYEnd, m_nYGridSize - 1); + nXEnd = tMax(nXEnd, 0); + nYEnd = tMax(nYEnd, 0); + + if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure) + { + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}, flagging as too short" << endl; + + // pProfile->SetTooShort(true); + m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); + } + } + } + + // For this coast, put all valid coastline-normal profiles (apart from the profiles at the start and end of the coast, since they have already been done) onto the raster grid. But if the profile is not long enough, crosses a coastline, hits dry land, or hits another profile, then mark the profile as invalid + int nValidProfiles = 0; + MarkProfilesOnGrid(nCoast, nValidProfiles); + + if (nValidProfiles == 0) + { + // Problem! No valid profiles, so quit + cerr << m_ulIter << ": " << ERR << "no coastline-normal profiles created" << endl; + return RTN_ERR_NO_PROFILES_2; + } + + // // DEBUG CODE =========================================================================================================== + // if (m_ulIter == 109) + // { + // string strOutFile = m_strOutPath; + // strOutFile += "00_profile_raster_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // + // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); + // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // + // int nn = 0; + // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // if (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()) + // pdRaster[nn] = -1; + // else + // { + // + // // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); + // } + // + // nn++; + // } + // } + // + // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_dMissingValue); + // int nRet1 = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet1 == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // } + // // DEBUG CODE =========================================================================================================== + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! Checks all line segments of a pair of coastline-normal profiles for intersection. If the lines intersect, returns true with the numbers of the line segments at which intersection occurs in nProfile1LineSegment and nProfile1LineSegment, the intersection point in dXIntersect and dYIntersect, and the 'average' seaward endpoint of the two intersecting profiles at dXAvgEnd and dYAvgEnd +//=============================================================================================================================== +bool CSimulation::bCheckForIntersection(CGeomProfile* const pVProfile1, CGeomProfile* const pVProfile2, int& nProfile1LineSegment, int& nProfile2LineSegment, double& dXIntersect, double& dYIntersect, double& dXAvgEnd, double& dYAvgEnd) +{ + // For both profiles, look at all line segments + int const nProfile1NumSegments = pVProfile1->nGetNumLineSegments(); + int const nProfile2NumSegments = pVProfile2->nGetNumLineSegments(); + // nProfile1Size = pVProfile1->nGetProfileSize(), + // nProfile2Size = pVProfile2->nGetProfileSize(); + + // assert(nProfile1Size == nProfile1NumSegments+1); + // assert(nProfile2Size == nProfile2NumSegments+1); + + for (int i = 0; i < nProfile1NumSegments; i++) + { + for (int j = 0; j < nProfile2NumSegments; j++) + { + // In external coordinates + double const dX1 = pVProfile1->pPtVGetPoints()->at(i).dGetX(); + double const dY1 = pVProfile1->pPtVGetPoints()->at(i).dGetY(); + double const dX2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetX(); + double const dY2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetY(); + + double const dX3 = pVProfile2->pPtVGetPoints()->at(j).dGetX(); + double const dY3 = pVProfile2->pPtVGetPoints()->at(j).dGetY(); + double const dX4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetX(); + double const dY4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetY(); + + // Uses Cramer's Rule to solve the equations. Modified from code at http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect (in turn based on Andre LeMothe's "Tricks of the Windows Game Programming Gurus") + double const dDiffX1 = dX2 - dX1; + double const dDiffY1 = dY2 - dY1; + double const dDiffX2 = dX4 - dX3; + double const dDiffY2 = dY4 - dY3; + + double dS = -999; + double dT = -999; + double dTmp = 0; + + dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2; + + if (! bFPIsEqual(dTmp, 0.0, TOLERANCE)) + dS = (-dDiffY1 * (dX1 - dX3) + dDiffX1 * (dY1 - dY3)) / dTmp; + + dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2; + + if (! bFPIsEqual(dTmp, 0.0, TOLERANCE)) + dT = (dDiffX2 * (dY1 - dY3) - dDiffY2 * (dX1 - dX3)) / dTmp; + + if (dS >= 0 && dS <= 1 && dT >= 0 && dT <= 1) + { + // Collision detected, calculate intersection coordinates + dXIntersect = dX1 + (dT * dDiffX1); + dYIntersect = dY1 + (dT * dDiffY1); + + // And calc the average end-point coordinates + dXAvgEnd = (dX2 + dX4) / 2; + dYAvgEnd = (dY2 + dY4) / 2; + + // Get the line segments at which intersection occurred + nProfile1LineSegment = i; + nProfile2LineSegment = j; + + // LogStream << "\t" << "INTERSECTION dX2 = " << dX2 << " dX4 = " << dX4 << " dY2 = " << dY2 << " dY4 = " << dY4 << endl; + return true; + } + } + } + + // No intersection + return false; +} + +//=============================================================================================================================== +//! For this coastline, marks all coastline-normal profiles (apart from the two 'special' ones at the start and end of the coast) onto the raster grid, i.e. rasterizes multi-line vector objects onto the raster grid. Note that this doesn't work if the vector has already been interpolated to fit on the grid i.e. if distances between vector points are just one cell apart +//=============================================================================================================================== +void CSimulation::MarkProfilesOnGrid(int const nCoast, int& nValidProfiles) +{ + // How many profiles on this coast? + int const nProfiles = m_VCoast[nCoast].nGetNumProfiles(); + + if (nProfiles == 0) + { + // This can happen if the coastline is very short, so just give a warning and carry on with the next coastline + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << WARN << m_ulIter << ": coast " << nCoast << " has no profiles" << endl; + + return; + } + + static bool bDownCoast = true; + + // Now do this for every profile, alternate between up-coast and down-coast directions + for (int n = 0; n < nProfiles; n++) + { + CGeomProfile* pProfile; + + if (bDownCoast) + pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n); + else + pProfile = m_VCoast[nCoast].pGetProfileWithUpCoastSeq(n); + + // Don't do this for the first and last profiles (i.e. the profiles at the start and end of the coast) since these are put onto the grid elsewhere + if (pProfile->bIsGridEdge()) + continue; + + int const nProfile = pProfile->nGetProfileID(); + + // If this profile has a problem, then forget about it + // if (! pProfile->bProfileOK()) + // { + // LogStream << m_ulIter << ": in MarkProfilesOnGrid() profile " << nProfile << " is not OK" << endl; + // continue; + // } + + int const nPoints = pProfile->nGetProfileSize(); + + if (nPoints < 2) + { + // Need at least two points in the profile, so this profile is invalid: mark it + m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true); + + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, has only " << nPoints << " points" << endl; + + continue; + } + + // OK, go for it: set up temporary vectors to hold the x-y coords (in grid CRS) of the cells which we will mark + vector VCellsToMark; + vector bVShared; + bool bTooShort = false; + bool bTruncatedSameCoast = false; + bool bHitCoast = false; + bool bHitLand = false; + bool bHitIntervention = false; + bool bHitAnotherProfile = false; + + CreateRasterizedProfile(nCoast, pProfile, &VCellsToMark, &bVShared, bTooShort, bTruncatedSameCoast, bHitCoast, bHitLand, bHitIntervention, bHitAnotherProfile); + + if ((bTruncatedSameCoast && (! ACCEPT_TRUNCATED_PROFILES)) || bTooShort || bHitCoast || bHitLand || bHitIntervention || bHitAnotherProfile || VCellsToMark.size() == 0) + continue; + + // This profile is fine + nValidProfiles++; + + for (unsigned int k = 0; k < VCellsToMark.size(); k++) + { + // Ignore duplicate points + if ((k > 0) && (VCellsToMark[k] == m_VCoast[nCoast].pGetProfile(nProfile)->pPtiGetLastCellInProfile())) + continue; + + // Mark each cell in the raster grid + int const nXTmp = VCellsToMark[k].nGetX(); + int const nYTmp = VCellsToMark[k].nGetY(); + m_pRasterGrid->m_Cell[nXTmp][nYTmp].SetCoastAndProfileID(nCoast, nProfile); + + // Store the raster grid coordinates in the profile object + m_VCoast[nCoast].pGetProfile(nProfile)->AppendCellInProfile(nXTmp, nYTmp); + + // Mark the shared (i.e. multi-line) parts of the profile (if any) + // if (bVShared[k]) + // { + // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(true); + // // LogStream << m_ulIter << ": profile " << j << " point " << k << " marked as shared" << endl; + // } + // else + // { + // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(false); + // // LogStream << m_ulIter << ": profile " << nProfile << " point " << k << " marked as NOT shared" << endl; + // } + } + + // Get the deep water wave height and orientation values at the end of the profile + double const dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveHeight(); + double const dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveAngle(); + double const dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWavePeriod(); + + // And store them for this profile + m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight); + m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle); + m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod); + } + + bDownCoast = ! bDownCoast; +} + +//=============================================================================================================================== +//! Given a pointer to a coastline-normal profile, returns an output vector of cells which are 'under' every line segment of the profile. If there is a problem with the profile (e.g. a rasterized cell is dry land or coast, or the profile has to be truncated) then we pass this back as an error code +//=============================================================================================================================== +void CSimulation::CreateRasterizedProfile(int const nCoast, CGeomProfile* pProfile, vector* pVIPointsOut, vector* pbVShared, bool& bTooShort, bool& bTruncatedSameCoast, bool& bHitCoast, bool& bHitLand, bool& bHitIntervention, bool& bHitAnotherProfile) +{ + int const nProfile = pProfile->nGetProfileID(); + int nSeg = 0; + int const nNumSegments = pProfile->nGetNumLineSegments(); + + pVIPointsOut->clear(); + + // LogStream << m_ulIter << ": in CreateRasterizedProfile() *pPtiStart for profile " << nProfile << " is [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "]" << endl; + int nXStartLast = INT_NODATA; + int nYStartLast = INT_NODATA; + int nXEndLast = INT_NODATA; + int nYEndLast = INT_NODATA; + + // Do for every segment of this profile + for (nSeg = 0; nSeg < nNumSegments; nSeg++) + { + // Do once for every line segment + CGeom2DIPoint PtiSegStart; + + if (nSeg == 0) + { + // If this is the first segment, use the coastline start point to prevent external CRS to grid CRS rounding errors + int const nCoastPoint = pProfile->nGetCoastPoint(); + PtiSegStart = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastPoint); + } + else + { + CGeom2DPoint const* pPtSegStart = pProfile->pPtGetPointInProfile(nSeg); + + // Convert from the external CRS to grid CRS + PtiSegStart = PtiExtCRSToGridRound(pPtSegStart); + } + + CGeom2DPoint const* pPtSegEnd = pProfile->pPtGetPointInProfile(nSeg + 1); // This is OK + + // Convert from the external CRS to grid CRS + CGeom2DIPoint const PtiSegEnd = PtiExtCRSToGridRound(pPtSegEnd); + + // Safety check + if (PtiSegStart == PtiSegEnd) + continue; + + int const nXStart = PtiSegStart.nGetX(); + int const nYStart = PtiSegStart.nGetY(); + int const nXEnd = PtiSegEnd.nGetX(); + int const nYEnd = PtiSegEnd.nGetY(); + + bool bShared = false; + + if (pProfile->nGetNumCoincidentProfilesInLineSegment(nSeg) > 1) + { + bShared = true; + + // If this is the second or more of several coincident line segments (i.e. it has the same start and end points as the previous line segment) then ignore it + if ((nXStart == nXStartLast) && (nYStart == nYStartLast) && (nXEnd == nXEndLast) && (nYEnd == nYEndLast)) + continue; + } + + // Interpolate between cells by a simple DDA line algorithm, see http://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm) Note that Bresenham's algorithm gave occasional gaps + double dXInc = nXEnd - nXStart; + double dYInc = nYEnd - nYStart; + double const dLength = tMax(tAbs(dXInc), tAbs(dYInc)); + + dXInc /= dLength; + dYInc /= dLength; + + double dX = nXStart; + double dY = nYStart; + + // Process each interpolated point + for (int m = 0; m <= nRound(dLength); m++) + { + int const nX = nRound(dX); + int const nY = nRound(dY); + + // Do some checking of this interpolated point, but only if this is not a grid-edge profile (these profiles are always valid) + if (! pProfile->bIsGridEdge()) + { + // Is the interpolated point within the valid raster grid? + if (! bIsWithinValidGrid(nX, nY)) + { + // It is outside the valid grid, so mark this profile and quit the loop + bTruncatedSameCoast = true; + + if (! ACCEPT_TRUNCATED_PROFILES) + pProfile->SetTruncatedSameCoast(true); + + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter << ": profile " << nProfile << " is invalid, truncated at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl; + + break; + } + + // Check again: is this cell (or an adjacent cell: does not matter which) already marked as 'under' a profile? + int nYTmp = nY+1; + if (nY+1 >= m_nYGridSize) + nYTmp = nY-1; + + if (m_pRasterGrid->m_Cell[nX][nY].bIsProfile() || m_pRasterGrid->m_Cell[nX][nYTmp].bIsProfile()) + { + // This cell or an adjacent cell, is 'under' a profile, so now check if the profile belongs to another coast + int const nHitProfileCoast1 = m_pRasterGrid->m_Cell[nX][nY].nGetProfileCoastID(); + int const nHitProfileCoast2 = m_pRasterGrid->m_Cell[nX][nYTmp].nGetProfileCoastID(); + + if ((nHitProfileCoast1 == nCoast) || (nHitProfileCoast2 == nCoast)) + { + // The profile belongs to the same coast, mark this profile as invalid + bHitAnotherProfile = true; + pProfile->SetHitAnotherProfile(true); + return; + } + } + + // If this is the first line segment of the profile, then once we are clear of the coastline (when m > PROFILE_CHECK_DIST_FROM_COAST), check if this profile hits land at this interpolated point. NOTE Get problems here since if the coastline vector has been heavily smoothed, this can result is 'false positives' profiles marked as invalid which are not actually invalid, because the profile hits land when m = 0 or m = 1. This results in some cells being flagged as profile cells which are actually inland + if (m > PROFILE_CHECK_DIST_FROM_COAST) + { + // Check this cell. Two diagonal(ish) raster lines can cross each other without any intersection, so must also test an adjacent cell for intersection (does not matter which adjacent cell) + if ((m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()) || (bIsWithinValidGrid(nX, nY + 1) && m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline())) + { + // We've hit a coastline so set a switch and mark the profile, then quit + bHitCoast = true; + pProfile->SetHitCoast(true); + int const nHitCoast = m_pRasterGrid->m_Cell[nX][nY].nGetCoastline(); + + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " is invalid, hit coast " << nHitCoast << " at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl; + + return; + } + + if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) + { + // We've hit dry land, so set a switch and mark the profile + bHitLand = true; + pProfile->SetHitLand(true); + + LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " HIT LAND at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() << ", SWL = " << m_dThisIterSWL << endl; + + return; + } + + if (m_pRasterGrid->m_Cell[nX][nY].nGetInterventionClass() != INT_NODATA) + { + // We've hit an intervention, so set a switch and mark the profile + bHitIntervention = true; + pProfile->SetHitIntervention(true); + + LogStream << m_ulIter << ": \tcoast " << nCoast << " profile " << nProfile << " HIT INTERVENTION at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() << ", SWL = " << m_dThisIterSWL << endl; + + return; + } + } + + // Now check to see if we hit another profile which is not a coincident normal to this normal + if (m_pRasterGrid->m_Cell[nX][nY].bIsProfile()) + { + // We've hit a raster cell which is already marked as 'under' a normal profile. Get the number of the profile which marked this cell, and the coast to hich this profile belongs + int const nHitProfile = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); + int const nHitProfileCoast = m_pRasterGrid->m_Cell[nX][nY].nGetProfileCoastID(); + + // Do both profiles belong to the same coast? + if (nCoast == nHitProfileCoast) + { + // Both profiles belong to the same coast. Is this the number of a coincident profile of this profile? + if (! pProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nHitProfile)) + { + // It isn't a coincident profile, so we have just hit an unrelated profile. Mark this profile as invalid and move on + pProfile->SetHitAnotherProfile(true); + bHitAnotherProfile = true; + + return; + } + } + } + } + + // Append this point to the output vector + pVIPointsOut->push_back(CGeom2DIPoint(nX, nY)); // Is in raster grid coordinates + pbVShared->push_back(bShared); + + // And increment for next time + dX += dXInc; + dY += dYInc; + } + + nXStartLast = nXStart; + nYStartLast = nYStart; + nXEndLast = nXEnd; + nYEndLast = nYEnd; + + if (bTruncatedSameCoast) + break; + } + + if (bTruncatedSameCoast) + { + if (nSeg < (nNumSegments - 1)) + // We are truncating the profile, so remove any line segments after this one + pProfile->TruncateLineSegments(nSeg); + + // Shorten the vector input. Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty + int const nLastX = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX(); + int const nLastY = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY(); + + pProfile->pPtGetPointInProfile(nSeg + 1)->SetX(dGridCentroidXToExtCRSX(nLastX)); + pProfile->pPtGetPointInProfile(nSeg + 1)->SetY(dGridCentroidYToExtCRSY(nLastY)); + } + + // // DEBUG CODE ===================================================================================== + // LogStream << "====================" << endl; + // LogStream << m_ulIter << ": for profile " << nProfile << " pPtiStart = [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] pPtiEnd = [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] pVIPointsOut->size() = " << pVIPointsOut->size() << endl; + // // for (int n = 0; n < static_cast(pVIPointsOut->size()); n++) + // // LogStream << "\t[" << pVIPointsOut->at(n).nGetX() << "][" << pVIPointsOut->at(n).nGetY() << "]" << endl; + // LogStream << "====================" << endl; + // // DEBUG CODE ===================================================================================== + + if (pVIPointsOut->size() < 3) + { + // Coastline-normal profiles cannot be very short (e.g. with less than 3 cells), since we cannot calculate along-profile slope properly for such short profiles + bTooShort = true; + pProfile->SetTooShort(true); + + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + { + // Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty + LogStream << m_ulIter << ": profile " << nProfile << " is invalid, is too short, only " << pVIPointsOut->size() << " points, HitLand?" << bHitLand << ". From [" << pVIPointsOut->at(0).nGetX() << "][" << pVIPointsOut->at(0).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(0).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(0).nGetY()) << "} to [" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX() << "][" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY()) << "}" << endl; + } + } +} + +//=============================================================================================================================== +//! Merges two profiles which intersect at their final (most seaward) line segments, seaward of their point of intersection +//=============================================================================================================================== +void CSimulation::MergeProfilesAtFinalLineSegments(int const nCoast, CGeomProfile* pFirstProfile, CGeomProfile* pSecondProfile, int const nFirstProfileLineSegments, int const nSecondProfileLineSegments, double const dIntersectX, double const dIntersectY, double const dAvgEndX, double const dAvgEndY) +{ + // The point of intersection is on the final (most seaward) line segment of both profiles. Put together a vector of coincident profile numbers (with no duplicates) for both profiles + int nCombinedLastSeg = 0; + vector> prVCombinedProfilesCoincidentProfilesLastSeg; + + for (unsigned int n = 0; n < pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->size(); n++) + { + pair prTmp; + prTmp.first = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).first; + prTmp.second = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).second; + + bool bFound = false; + + for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++) + { + if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first) + { + bFound = true; + break; + } + } + + if (! bFound) + { + prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp); + nCombinedLastSeg++; + } + } + + for (unsigned int n = 0; n < pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->size(); n++) + { + pair prTmp; + prTmp.first = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).first; + prTmp.second = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).second; + + bool bFound = false; + + for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++) + { + if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first) + { + bFound = true; + break; + } + } + + if (! bFound) + { + prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp); + nCombinedLastSeg++; + } + } + + // Increment the number of each line segment + for (int m = 0; m < nCombinedLastSeg; m++) + prVCombinedProfilesCoincidentProfilesLastSeg[m].second++; + + vector> prVFirstProfileCoincidentProfilesLastSeg = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1); + vector> prVSecondProfileCoincidentProfilesLastSeg = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1); + int const nNumFirstProfileCoincidentProfilesLastSeg = static_cast(prVFirstProfileCoincidentProfilesLastSeg.size()); + int const nNumSecondProfileCoincidentProfilesLastSeg = static_cast(prVSecondProfileCoincidentProfilesLastSeg.size()); + + // LogStream << m_ulIter << ": END-SEGMENT INTERSECTION between profiles " << nFirstProfile << " and " << nSecondProfile << " at line segment " << nFirstProfileLineSegments-1 << "/" << nFirstProfileLineSegments-1 << ", and line segment " << nSecondProfileLineSegments-1 << "/" << nSecondProfileLineSegments-1 << ", respectively. Both truncated at [" << dIntersectX << ", " << dIntersectY << "] then profiles {" << nFirstProfile << "} and {" << nSecondProfile << "} extended to [" << dAvgEndX << ", " << dAvgEndY << "]" << endl; + + // Truncate the first profile, and all co-incident profiles, at the point of intersection + for (int n = 0; n < nNumFirstProfileCoincidentProfilesLastSeg; n++) + { + int const nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[n].first; + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + int const nProfileLength = pThisProfile->nGetProfileSize(); + + // This is the final line segment of the first 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile + pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY); + } + + // Truncate the second profile, and all co-incident profiles, at the point of intersection + for (int n = 0; n < nNumSecondProfileCoincidentProfilesLastSeg; n++) + { + int const nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[n].first; + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + int const nProfileLength = pThisProfile->nGetProfileSize(); + + // This is the final line segment of the second 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile + pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY); + } + + // Append a new straight line segment to the existing line segment(s) of the first profile, and to all co-incident profiles + for (int nThisLineSeg = 0; nThisLineSeg < nNumFirstProfileCoincidentProfilesLastSeg; nThisLineSeg++) + { + int const nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[nThisLineSeg].first; + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + + // Update this profile + pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY); + + // Append details of the combined profiles + pThisProfile->AppendLineSegment(); + + for (int m = 0; m < nCombinedLastSeg; m++) + pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]); + } + + // Append a new straight line segment to the existing line segment(s) of the second profile, and to all co-incident profiles + for (int nThisLineSeg = 0; nThisLineSeg < nNumSecondProfileCoincidentProfilesLastSeg; nThisLineSeg++) + { + int const nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[nThisLineSeg].first; + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + + // Update this profile + pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY); + + // Append details of the combined profiles + pThisProfile->AppendLineSegment(); + + for (int m = 0; m < nCombinedLastSeg; m++) + pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]); + } + + // // DEBUG CODE **************************************************************** + // int nFirstProfileLineSeg= pFirstProfile->nGetNumLineSegments(); + // int nSecondProfileLineSeg = pSecondProfile->nGetNumLineSegments(); + // + // LogStream << "\tProfile {" << nFirstProfile << "} now has " << nFirstProfileLineSeg << " line segments" << endl; + // for (int m = 0; m < nFirstProfileLineSeg; m++) + // { + // vector > prVCoincidentProfiles = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(m); + // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nFirstProfile << "} are {"; + // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++) + // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] "; + // LogStream << " }" << endl; + // } + // LogStream << "\tProfile {" << nSecondProfile << "} now has " << nSecondProfileLineSeg << " line segments" << endl; + // for (int m = 0; m < nSecondProfileLineSeg; m++) + // { + // vector > prVCoincidentProfiles = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(m); + // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nSecondProfile << "} are {"; + // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++) + // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] "; + // LogStream << " }" << endl; + // } + // // DEBUG CODE ****************************************************************** +} + +//=============================================================================================================================== +//! Truncates one intersecting profile at the point of intersection, and retains the other profile +//=============================================================================================================================== +void CSimulation::TruncateOneProfileRetainOtherProfile(int const nCoast, CGeomProfile* pProfileToTruncate, CGeomProfile* pProfileToRetain, double dIntersectX, double dIntersectY, int nProfileToTruncateIntersectLineSeg, int nProfileToRetainIntersectLineSeg, bool const bAlreadyPresent) +{ + // // Occasionally, profiles cross each other, with the crossing not detected. So check for intersection between pProfileToTruncate and all profiles (starting from the last i.e. in reverse order) in all segments (starting from the last i.e. in reverse order) of pProfileToRetain's CGeomMultiLine + // bool bFound = false; + // int nNumSegProfRetain = pProfileToRetain->nGetNumLineSegments(); + // for (int nSeg = nNumSegProfRetain-1; nSeg >= 0; nSeg--) + // { + // if (bFound) + // break; + // + // int nNumProfInSeg = pProfileToRetain->nGetNumCoincidentProfilesInLineSegment(nSeg); + // for (int nProf = nNumProfInSeg-1; nProf >= 0; nProf--) + // { + // int nThisProf = pProfileToRetain->nGetCoincidentProfileForLineSegment(nSeg, nProf); + // CGeomProfile* pThisProf = m_VCoast[nCoast].pGetProfile(nThisProf); + // + // int nProfToTruncLineSeg = 0; + // int nThisProfLineSeg = 0; + // double dTmpIntersectX = 0; + // double dTmpIntersectY = 0; + // double dAvgEndX = 0; + // double dAvgEndY = 0; + // + // if (bCheckForIntersection(pProfileToTruncate, pThisProf, nProfToTruncLineSeg, nThisProfLineSeg, dTmpIntersectX, dTmpIntersectY, dAvgEndX, dAvgEndY)) + // { + // // An intersection was found: so the profile with which pProfileToTruncate intersects becomes the new pProfileToRetain, and dIntersectX, dIntersectY, nProfileToTruncateIntersectLineSeg, and nProfileToRetainIntersectLineSeg are also changed + // pProfileToRetain = pThisProf; + // dIntersectX = dTmpIntersectX; + // dIntersectY = dTmpIntersectY; + // nProfileToRetainIntersectLineSeg = nThisProfLineSeg; + // nProfileToTruncateIntersectLineSeg = nProfToTruncLineSeg; + // + // bFound = true; + // break; + // } + // } + // } + + // Insert the intersection point into the main retain-profile if it is not already in the profile, and do the same for all co-incident profiles of the main retain-profile. Also add details of the to-truncate profile (and all its coincident profiles) to every line segment of the main to-retain profile which is seaward of the point of intersection + int const nRet = nInsertPointIntoProfilesIfNeededThenUpdate(nCoast, pProfileToRetain, dIntersectX, dIntersectY, nProfileToRetainIntersectLineSeg, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, bAlreadyPresent); + + if (nRet != RTN_OK) + { + // LogStream << m_ulIter << ": error in nInsertPointIntoProfilesIfNeededThenUpdate()" << endl; + return; + } + + // Get all profile points of the main retain-profile seawards from the intersection point, and do the same for the corresponding line segments (including coincident profiles). This also includes details of the main to-truncate profile (and all its coincident profiles) + vector PtVProfileLastPart; + vector>> prVLineSegLastPart; + + if (bAlreadyPresent) + { + PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg); + prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg); + } + + else + { + PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg + 1); + prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg + 1); + } + + // assert(PtVProfileLastPart.size() > 1); + // assert(prVLineSegLastPart.size() > 0); + + // Truncate the truncate-profile at the point of intersection, and do the same for all its co-incident profiles. Then append the profile points of the main to-retain profile seaward from the intersection point, and do the same for the corresponding line segments (including coincident profiles) + TruncateProfileAndAppendNew(nCoast, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, &PtVProfileLastPart, &prVLineSegLastPart); + + // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetProfileSize() > 1); + // assert(pProfileToRetain->nGetNumLineSegments() > 0); + // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetNumLineSegments() > 0); +} + +//=============================================================================================================================== +//! Inserts an intersection point into the profile that is to be retained, if that point is not already present in the profile, then does the same for all co-incident profiles. Finally adds the numbers of the to-truncate profile (and all its coincident profiles) to the seaward line segments of the to-retain profile and all its coincident profiles +//=============================================================================================================================== +int CSimulation::nInsertPointIntoProfilesIfNeededThenUpdate(int const nCoast, CGeomProfile* pProfileToRetain, double const dIntersectX, double const dIntersectY, int const nProfileToRetainIntersectLineSeg, CGeomProfile* pProfileToTruncate, int const nProfileToTruncateIntersectLineSeg, bool const bAlreadyPresent) +{ + // // DEBUG CODE **************************************************************** + // // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurred + // vector > prVRetainCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); + // int nNumRetainCoincidentCHECK1 = prVRetainCoincidentProfilesCHECK1.size(); + // for (int nn = 0; nn < nNumRetainCoincidentCHECK1; nn++) + // { + // int nThisProfile = prVRetainCoincidentProfilesCHECK1[nn].first; + // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) + // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; + // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; + // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) + // { + // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); + // LogStream << "{ "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // + // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) + // { + // CGeom2DPoint + // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), + // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); + // + // if (Pt1 == Pt2) + // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; + // } + // } + // + // // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred + // vector > prVTruncateCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg); + // int nNumTruncateCoincidentCHECK1 = prVTruncateCoincidentProfilesCHECK1.size(); + // for (int nn = 0; nn < nNumTruncateCoincidentCHECK1; nn++) + // { + // int nThisProfile = prVTruncateCoincidentProfilesCHECK1[nn].first; + // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToTruncate ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) + // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; + // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; + // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) + // { + // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); + // LogStream << "{ "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // + // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) + // { + // CGeom2DPoint + // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), + // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); + // + // if (Pt1 == Pt2) + // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; + // } + // } + // // DEBUG CODE ****************************************************************** + + int const nProfileToRetain = pProfileToRetain->nGetProfileID(); + + // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurs + vector> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); + int const nNumCoincident = static_cast(prVCoincidentProfiles.size()); + vector nLineSegAfterIntersect(nNumCoincident, -1); // The line segment after the point of intersection, for each co-incident profile + + // Do this for the main profile and all profiles which are co-incident for this line segment + for (int nn = 0; nn < nNumCoincident; nn++) + { + int const nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile + int const nThisLineSeg = prVCoincidentProfiles[nn].second; // The line segment of this profile + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + + // Is the intersection point already present in the to-retain profile? + if (! bAlreadyPresent) + { + // It is not already present, so insert it and also update the associated multi-line + if (! pThisProfile->bInsertIntersection(dIntersectX, dIntersectY, nThisLineSeg)) + { + // Error + LogStream << WARN << m_ulIter << ": cannot insert a line segment after the final line segment (" << nThisLineSeg << ") for " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile (" << nThisProfile << "), abandoning" << endl; + + return RTN_ERR_CANNOT_INSERT_POINT; + } + + // LogStream << "\tIntersection point NOT already in " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile {" << nThisProfile << "}, inserted it as point " << nThisLineSeg+1 << endl; + } + + // Get the line segment after intersection + nLineSegAfterIntersect[nn] = nThisLineSeg + 1; + } + + // for (int nn = 0; nn < nNumCoincident; nn++) + // LogStream << "\tFor profile " << prVCoincidentProfiles[nn].first << " line segment [" << nLineSegAfterIntersect[nn] << "] is immediately after the intersection point" << endl; + + // Get the coincident profiles for the to-truncate profile, at the line segment where intersection occurs + vector> prVToTruncateCoincidentProfiles = *pProfileToTruncate->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg); + int const nNumToTruncateCoincident = static_cast(prVToTruncateCoincidentProfiles.size()); + + // Now add the number of the to-truncate profile, and all its coincident profiles, to all line segments which are seaward of the point of intersection. Do this for the main profile and all profiles which are co-incident for this line segment + for (int nn = 0; nn < nNumCoincident; nn++) + { + int const nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + + // Get the number of line segments for this to-retain profile (will have just increased, if we just inserted a point) + int const nNumLineSegs = pThisProfile->nGetNumLineSegments(); + + // Do for all line segments seaward of the point of intersection + for (int nLineSeg = nLineSegAfterIntersect[nn], nIncr = 0; nLineSeg < nNumLineSegs; nLineSeg++, nIncr++) + { + // // This can happen occasionally + // if (nThisProfile == nProfileToTruncateIntersectLineSeg) + // { + // LogStream << "\t*** ERROR nThisProfile = " << nThisProfile << " nProfileToTruncateIntersectLineSeg = " << nProfileToTruncateIntersectLineSeg << ", ignoring" << endl; + // pThisProfile->SetHitAnotherProfile(true); + // continue; + // } + + // Add the number of the to-truncate profile, and all its coincident profiles, to this line segment + for (int m = 0; m < nNumToTruncateCoincident; m++) + { + int const nProfileToAdd = prVToTruncateCoincidentProfiles[m].first; + int const nProfileToAddLineSeg = prVToTruncateCoincidentProfiles[m].second; + + // LogStream << "\tAdding " << (nProfileToAdd == nProfileToTruncateIntersectLineSeg ? "main" : "co-incident") << " truncate-profile " << nProfileToAdd << ", line segment [" << nProfileToAddLineSeg + nIncr << "] to line segment " << nLineSeg << " of " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " to-retain profile " << nThisProfile << endl; + + pThisProfile->AddCoincidentProfileToExistingLineSegment(nLineSeg, nProfileToAdd, nProfileToAddLineSeg + nIncr); + } + } + } + + // // DEBUG CODE **************************************************************** + // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred + // vector > prVCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nProfileToRetain)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg); + // int nNumCoincidentCHECK2 = prVCoincidentProfilesCHECK2.size(); + // for (int nn = 0; nn < nNumCoincidentCHECK2; nn++) + // { + // int nThisProfile = prVCoincidentProfilesCHECK2[nn].first; + // LogStream << "\tAFTER nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToRetain ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) + // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; + // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are "; + // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++) + // { + // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg); + // LogStream << "{ "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // + // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) + // { + // CGeom2DPoint + // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), + // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); + // + // if (Pt1 == Pt2) + // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; + // } + // } + // // DEBUG CODE ****************************************************************** + + return RTN_OK; +} + +//=============================================================================================================================== +//! Truncate a profile at the point of intersection, and do the same for all its co-incident profiles +//=============================================================================================================================== +void CSimulation::TruncateProfileAndAppendNew(int const nCoast, CGeomProfile* pProfileToRetain, int const nMainProfileIntersectLineSeg, vector const* pPtVProfileLastPart, vector>> const* pprVLineSegLastPart) +{ + // // DEBUG CODE **************************************************************** + // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred + // vector > prVCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); + // int nNumCoincidentCHECK1 = prVCoincidentProfilesCHECK1.size(); + // + // LogStream << "\tTruncating profile {" << nMainProfile << "}, intersection is at [" << dIntersectX << ", " << dIntersectY << "] in line segment " << nMainProfileIntersectLineSeg << endl; + // for (int nn = 0; nn < nNumCoincidentCHECK1; nn++) + // { + // int nThisProfile = prVCoincidentProfilesCHECK1[nn].first; + // LogStream << "\tBEFORE TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ("; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) + // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; + // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are "; + // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++) + // { + // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg); + // LogStream << "{ "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // + // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) + // { + // CGeom2DPoint + // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), + // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); + // + // if (Pt1 == Pt2) + // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; + // } + // } + // LogStream << "\tPart-profile to append is "; + // for (int mm = 0; mm < pPtVProfileLastPart->size(); mm++) + // LogStream << "[" << pPtVProfileLastPart->at(mm).dGetX() << ", " << pPtVProfileLastPart->at(mm).dGetY() << "] "; + // LogStream << endl; + // LogStream << "\tPart line-segment to append is "; + // for (int mm = 0; mm < pprVLineSegLastPart->size(); mm++) + // { + // vector > prVTmp = pprVLineSegLastPart->at(mm); + // LogStream << "{ "; + // for (int nn = 0; nn < prVTmp.size(); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // // DEBUG CODE ****************************************************************** + + // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurs + vector> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); + int const nNumCoincident = static_cast(prVCoincidentProfiles.size()); + + for (int nn = 0; nn < nNumCoincident; nn++) + { + // Do this for the main to-truncate profile, and do the same for all its co-incident profiles + int const nThisProfile = prVCoincidentProfiles[nn].first; + int const nThisProfileLineSeg = prVCoincidentProfiles[nn].second; + CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile); + + // if (nThisProfile == nMainProfile) + // assert(nThisProfileLineSeg == nMainProfileIntersectLineSeg); + + // Truncate the profile + // LogStream << "\tTruncating " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} at line segment " << nThisProfileLineSeg+1 << endl; + pThisProfile->TruncateProfile(nThisProfileLineSeg + 1); + + // Reduce the number of line segments for this profile + pThisProfile->TruncateLineSegments(nThisProfileLineSeg + 1); + + // Append the profile points from the last part of the retain-profile + for (unsigned int mm = 0; mm < pPtVProfileLastPart->size(); mm++) + { + CGeom2DPoint const Pt = pPtVProfileLastPart->at(mm); + pThisProfile->AppendPointInProfile(&Pt); + } + + // Append the line segments, and their co-incident profile numbers, from the last part of the retain-profile + for (unsigned int mm = 0; mm < pprVLineSegLastPart->size(); mm++) + { + vector> prVTmp = pprVLineSegLastPart->at(mm); + + pThisProfile->AppendLineSegment(&prVTmp); + } + + // Fix the line seg numbers for this profile + vector nVProf; + vector nVProfsLineSeg; + + for (int nSeg = 0; nSeg < pThisProfile->nGetNumLineSegments(); nSeg++) + { + for (int nCoinc = 0; nCoinc < pThisProfile->nGetNumCoincidentProfilesInLineSegment(nSeg); nCoinc++) + { + int const nProf = pThisProfile->nGetProf(nSeg, nCoinc); + int const nProfsLineSeg = pThisProfile->nGetProfsLineSeg(nSeg, nCoinc); + + auto it = find(nVProf.begin(), nVProf.end(), nProf); + + if (it == nVProf.end()) + { + // Not found + nVProf.push_back(nProf); + nVProfsLineSeg.push_back(nProfsLineSeg); + } + + else + { + // Found + int const nPos = static_cast(it - nVProf.begin()); + int nNewProfsLineSeg = nVProfsLineSeg[nPos]; + nNewProfsLineSeg++; + + nVProfsLineSeg[nPos] = nNewProfsLineSeg; + pThisProfile->SetProfsLineSeg(nSeg, nCoinc, nNewProfsLineSeg); + } + } + } + + // assert(pThisProfile->nGetProfileSize() > 1); + } + + // // DEBUG CODE **************************************************************** + // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred + // vector > prVToTruncateCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg); + // int nNumToTruncateCoincidentCHECK2 = prVToTruncateCoincidentProfilesCHECK2.size(); + // for (int nn = 0; nn < nNumToTruncateCoincidentCHECK2; nn++) + // { + // int nThisProfile = prVToTruncateCoincidentProfilesCHECK2[nn].first; + // LogStream << "\tAFTER TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ("; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++) + // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] "; + // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are "; + // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++) + // { + // vector > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm); + // LogStream << "{ "; + // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++) + // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] "; + // LogStream << "} "; + // } + // LogStream << endl; + // + // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++) + // { + // CGeom2DPoint + // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint), + // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1); + // + // if (Pt1 == Pt2) + // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl; + // } + // } + // // DEBUG CODE ****************************************************************** +} + diff --git a/src/gis_raster.cpp b/src/gis_raster.cpp index da137c862..4f2d83cf3 100644 --- a/src/gis_raster.cpp +++ b/src/gis_raster.cpp @@ -1,2700 +1,2700 @@ -/*! - \file gis_raster.cpp - \brief These functions use GDAL (at least version 2) to read and write raster - GIS files in several formats - \details TODO 001 A more detailed description of these routines. - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - - This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include - -#include - -#include -using std::atan2; -using std::hypot; -using std::isfinite; -using std::isnan; -using std::sqrt; - -#include -using std::vector; - -#include -using std::cerr; -using std::endl; -using std::ios; - -#include -using std::ifstream; - -#include -using std::stringstream; - -#include -using std::to_string; - -#include -#include -#include -#include -#include -#include - -#include "2di_point.h" -#include "cme.h" -#include "coast.h" -#include "simulation.h" -#include "spatial_interpolation.h" - -//=============================================================================================================================== -//! Initialize GDAL with performance optimizations -//=============================================================================================================================== -void CSimulation::InitializeGDALPerformance(void) { - // Configure GDAL for optimal performance - // Enable GDAL threading - use all available CPU cores -#ifdef _OPENMP - CPLSetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); -#else - CPLSetConfigOption("GDAL_NUM_THREADS", "4"); // Fallback for non-OpenMP builds -#endif - - // Optimize GDAL memory usage and caching - CPLSetConfigOption("GDAL_CACHEMAX", - "1.5GB"); // 2GB cache for large grids (was 1GB) - CPLSetConfigOption("GDAL_DISABLE_READDIR_ON_OPEN", - "EMPTY_DIR"); // Faster file access - CPLSetConfigOption("VSI_CACHE", "TRUE"); // Enable virtual file system cache - CPLSetConfigOption("VSI_CACHE_SIZE", "512MB"); // 256MB VSI cache - - // Block and chunk optimizations for raster operations - CPLSetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"); - CPLSetConfigOption("GDAL_RASTERIO_RESAMPLING", - "CUBIC"); // Better for coastal DEM data - - // Grid creation optimizations (for GDALGridCreate performance) - CPLSetConfigOption("GDAL_GRID_MAX_POINTS_PER_QUADTREE_LEAF", "1024"); - // Increased from 512 - CPLSetConfigOption("GDAL_GRID_POINT_COUNT_THRESHOLD", - "100"); // New 2024 option - - // Thread-safe dataset access (GDAL 3.10+) - CPLSetConfigOption("GDAL_DATASET_CACHE_SIZE", "64"); // Cache more datasets - - // Compression optimizations for output - CPLSetConfigOption("GDAL_TIFF_OVR_BLOCKSIZE", - "512"); // Optimal for coastal data - - // Memory allocator optimization for multi-threading - CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", "YES"); - - // Disable GDAL warnings for cleaner output (optional) - // CPLSetConfigOption("CPL_LOG", "/dev/null"); - // Debugging (remove in production) - // CPLSetConfigOption("CPL_DEBUG", "ON"); - // CPLSetConfigOption("GDAL_DEBUG", "ON"); - - m_bGDALOptimisations = true; -} - - //=============================================================================================================================== -//! Reads a raster DEM of basement elevation data to the Cell array -//=============================================================================================================================== -int CSimulation::nReadRasterBasementDEM(void) -{ - // Initialize GDAL performance settings (only needs to be done once) - static bool bGDALInitialized = false; - - if (! bGDALInitialized) - { - InitializeGDALPerformance(); - bGDALInitialized = true; - } - - // Use GDAL to create a dataset object, which then opens the DEM file - GDALDataset *pGDALDataset = static_cast(GDALOpen(m_strInitialBasementDEMFile.c_str(), GA_ReadOnly)); - - if (NULL == pGDALDataset) - { - // Can't open file (note will already have sent GDAL error message to stdout) - cerr << ERR << "cannot open " << m_strInitialBasementDEMFile << " for input: " << CPLGetLastErrorMsg() << endl; - return RTN_ERR_DEMFILE; - } - - // Opened OK, so get GDAL basement DEM dataset information - m_strGDALBasementDEMDriverCode = pGDALDataset->GetDriver()->GetDescription(); - m_strGDALBasementDEMDriverDesc = pGDALDataset->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); - m_strGDALBasementDEMProjection = pGDALDataset->GetProjectionRef(); - - if (m_strGDALBasementDEMProjection.empty()) - { - // TODO - m_strGDALBasementDEMProjection = ""; - // pGDALDataset->SetProjectionRef(m_strGDALBasementDEMProjection); - -// ENGCRS["Plane", -// EDATUM["Unknown engineering datum"], -// CS[Cartesian,2], -// AXIS["(E)",east, -// ORDER[1], -// LENGTHUNIT["Meter",1]], -// AXIS["(N)",north, -// ORDER[2], -// LENGTHUNIT["Meter",1]]] - } - else - { - // We have reference units, so check that they are in metres (note US spelling) - if (! m_strGDALBasementDEMProjection.empty()) - { - string const strTmp = strToLower(&m_strGDALBasementDEMProjection); - - if ((strTmp.find("meter") == string::npos) && (strTmp.find("metre") == string::npos)) - { - // error: x-y values must be in metres - cerr << ERR << "GIS file x-y values (" << m_strGDALBasementDEMProjection << ") in " << m_strInitialBasementDEMFile << " must be in metres" << endl; - return RTN_ERR_DEMFILE; - } - } - } - - // Now get dataset size, and do some rudimentary checks - m_nXGridSize = pGDALDataset->GetRasterXSize(); - - if (m_nXGridSize == 0) - { - // Error: silly number of columns specified - cerr << ERR << "invalid number of columns (" << m_nXGridSize << ") in " << m_strInitialBasementDEMFile << endl; - return RTN_ERR_DEMFILE; - } - - m_nYGridSize = pGDALDataset->GetRasterYSize(); - - if (m_nYGridSize == 0) - { - // Error: silly number of rows specified - cerr << ERR << "invalid number of rows (" << m_nYGridSize << ") in " << m_strInitialBasementDEMFile << endl; - return RTN_ERR_DEMFILE; - } - - // Get geotransformation info (see http://www.gdal.org/classGDALDataset.html) - if (CE_Failure == pGDALDataset->GetGeoTransform(m_dGeoTransform)) - { - // Can't get geotransformation (note will already have sent GDAL error message to stdout) - cerr << ERR << CPLGetLastErrorMsg() << " in " << m_strInitialBasementDEMFile << endl; - return RTN_ERR_DEMFILE; - } - - // CoastalME can only handle rasters that are oriented N-S and W-E. (If you need to work with a raster that is oriented differently, then you must rotate it before running CoastalME). So here we check whether row rotation (m_dGeoTransform[2]) and column rotation (m_dGeoTransform[4]) are both zero. See https://gdal.org/tutorials/geotransforms_tut.html - if ((! bFPIsEqual(m_dGeoTransform[2], 0.0, TOLERANCE)) || (! bFPIsEqual(m_dGeoTransform[4], 0.0, TOLERANCE))) - { - // Error: not oriented NS and W-E - cerr << ERR << m_strInitialBasementDEMFile << " is not oriented N-S and W-E. Row rotation = " << m_dGeoTransform[2] << " and column rotation = " << m_dGeoTransform[4] << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // Get the X and Y cell sizes, in external CRS units. Note that while the cell is supposed to be square, it may not be exactly so due to oddities with some GIS calculations - double const dCellSideX = tAbs(m_dGeoTransform[1]); - double const dCellSideY = tAbs(m_dGeoTransform[5]); - - // Check that the cell is more or less square - if (! bFPIsEqual(dCellSideX, dCellSideY, 1e-2)) - { - // Error: cell is not square enough - cerr << ERR << "cell is not square in " << m_strInitialBasementDEMFile << ", is " << dCellSideX << " x " << dCellSideY << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // Calculate the average length of cell side, the cell's diagonal, and the area of a cell (in external CRS units) - m_dCellSide = (dCellSideX + dCellSideY) / 2.0; - m_dCellArea = m_dCellSide * m_dCellSide; - m_dCellDiagonal = hypot(m_dCellSide, m_dCellSide); - - // And calculate the inverse values - m_dInvCellSide = 1 / m_dCellSide; - m_dInvCellDiagonal = 1 / m_dCellDiagonal; - - // Save some values in external CRS - m_dNorthWestXExtCRS = m_dGeoTransform[0] - (m_dGeoTransform[1] / 2); - m_dNorthWestYExtCRS = m_dGeoTransform[3] - (m_dGeoTransform[5] / 2); - m_dSouthEastXExtCRS = m_dGeoTransform[0] + (m_nXGridSize * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2); - m_dSouthEastYExtCRS = m_dGeoTransform[3] + (m_nYGridSize * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2); - - // And calc the grid area in external CRS units - m_dExtCRSGridArea = tAbs(m_dNorthWestXExtCRS - m_dSouthEastXExtCRS) * tAbs(m_dNorthWestYExtCRS * m_dSouthEastYExtCRS); - - // Now get GDAL raster band information - GDALRasterBand *pGDALBand = pGDALDataset->GetRasterBand(1); - int nBlockXSize = 0, nBlockYSize = 0; - pGDALBand->GetBlockSize(&nBlockXSize, &nBlockYSize); - m_strGDALBasementDEMDataType = GDALGetDataTypeName(pGDALBand->GetRasterDataType()); - - // If we have value units, then check them - string const strUnits = pGDALBand->GetUnitType(); - - if ((!strUnits.empty()) && (strUnits.find('m') == string::npos)) - { - // Error: value units must be m - cerr << ERR << "DEM vertical units are (" << strUnits << " ) in " << m_strInitialBasementDEMFile << ", should be 'm'" << endl; - return RTN_ERR_DEMFILE; - } - - // If present, get the missing value (NODATA) setting - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails - double const dMissingValue = pGDALBand->GetNoDataValue(); // Will fail for some formats - CPLPopErrorHandler(); - - if (! bFPIsEqual(dMissingValue, m_dMissingValue, TOLERANCE)) - { - cerr << " " << NOTE << "NODATA value in " << m_strInitialBasementDEMFile << " is " << dMissingValue << "\n instead using CoastalME's default floating-point NODATA value " << m_dMissingValue << endl; - } - - // Next allocate memory for a 2D array of raster cell objects: tell the user what is happening - AnnounceAllocateMemory(); - int const nRet = m_pRasterGrid->nCreateGrid(); - - if (nRet != RTN_OK) - return nRet; - - // Allocate memory for a 1D floating-point array, to hold the scan line for GDAL - double *pdScanline = new double[m_nXGridSize]; - - if (NULL == pdScanline) - { - // Error, can't allocate memory - cerr << ERR << "cannot allocate memory for " << m_nXGridSize << " x 1D array" << endl; - return (RTN_ERR_MEMALLOC); - } - - // Now read in the data - for (int j = 0; j < m_nYGridSize; j++) - { - // Read scanline - if (CE_Failure == pGDALBand->RasterIO(GF_Read, 0, j, m_nXGridSize, 1, pdScanline, m_nXGridSize, 1, GDT_Float64, 0, 0, NULL)) - { - // Error while reading scanline - cerr << ERR << CPLGetLastErrorMsg() << " in " << m_strInitialBasementDEMFile << endl; - return RTN_ERR_DEMFILE; - } - - // All OK, so read scanline into cell elevations (including any missing values) - for (int i = 0; i < m_nXGridSize; i++) - { - double dTmp = pdScanline[i]; - - if ((isnan(dTmp)) || (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) - dTmp = m_dMissingValue; - - m_pRasterGrid->m_Cell[i][j].SetBasementElev(dTmp); - } - } - - // Finished, so get rid of dataset object - GDALClose(pGDALDataset); - - // Get rid of memory allocated to this array - delete[] pdScanline; - - return RTN_OK; -} - -//=============================================================================================================================== -//! Mark cells which are at the edge of a bounding box which represents the -//! valid part of the grid, as defined by the basement layer. The valid part of -//! the grid may be the whole grid, or only part of the whole grid. The bounding -//! box may be an irregular shape (but may not have re-entrant edges): simple -//! shapes are more likely to work correctly -//=============================================================================================================================== -int CSimulation::nMarkBoundingBoxEdgeCells(void) { - // The bounding box must touch the edge of the grid at least once on each side - // of the grid, so store these points. Search in a clockwise direction around - // the edge of the grid - vector VPtiBoundingBoxCorner; - - // Start with the top (north) edge - bool bFound = false; - - for (int nX = 0; nX < m_nXGridSize; nX++) { - if (bFound) - break; - - for (int nY = 0; nY < m_nYGridSize; nY++) { - if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - CGeom2DIPoint const PtiTmp(nX, nY); - VPtiBoundingBoxCorner.push_back(PtiTmp); - bFound = true; - break; - } - } - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": north (top) edge of bounding box not found" - << endl; - - return RTN_ERR_BOUNDING_BOX; - } - - // Do the same for the right (east) edge - bFound = false; - - for (int nY = 0; nY < m_nYGridSize; nY++) { - if (bFound) - break; - - for (int nX = m_nXGridSize - 1; nX >= 0; nX--) { - if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - CGeom2DIPoint const PtiTmp(nX, nY); - VPtiBoundingBoxCorner.push_back(PtiTmp); - bFound = true; - break; - } - } - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": east (right) edge of bounding box not found" - << endl; - - return RTN_ERR_BOUNDING_BOX; - } - - // Do the same for the south (bottom) edge - bFound = false; - - for (int nX = m_nXGridSize - 1; nX >= 0; nX--) { - if (bFound) - break; - - for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { - if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - CGeom2DIPoint const PtiTmp(nX, nY); - VPtiBoundingBoxCorner.push_back(PtiTmp); - bFound = true; - break; - } - } - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": south (bottom) edge of bounding box not found" - << endl; - - return RTN_ERR_BOUNDING_BOX; - } - - // And finally repeat for the west (left) edge - bFound = false; - - for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { - if (bFound) - break; - - for (int nX = 0; nX < m_nXGridSize; nX++) { - if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - CGeom2DIPoint const PtiTmp(nX, nY); - VPtiBoundingBoxCorner.push_back(PtiTmp); - bFound = true; - break; - } - } - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_ALL) - LogStream << m_ulIter << ": west (left) edge of bounding box not found" - << endl; - - return RTN_ERR_BOUNDING_BOX; - } - - // OK, so we have a point on each side of the grid, so start at this point and - // find the edges of the bounding box. Go round in a clockwise direction: top - // (north) edge first - for (int nX = VPtiBoundingBoxCorner[0].nGetX(); - nX <= VPtiBoundingBoxCorner[1].nGetX(); nX++) { - bFound = false; - - for (int nY = VPtiBoundingBoxCorner[0].nGetY(); nY < m_nYGridSize; nY++) { - if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - m_ulMissingValueBasementCells++; - continue; - } - - // Found a bounding box edge cell - m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(NORTH); - - m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); - m_VEdgeCellEdge.push_back(NORTH); - - bFound = true; - break; - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream - << m_ulIter - << ": could not find a bounding box edge cell for grid column " - << nX << endl; - - return RTN_ERR_BOUNDING_BOX; - } - } - - // Right (east) edge - for (int nY = VPtiBoundingBoxCorner[1].nGetY(); - nY <= VPtiBoundingBoxCorner[2].nGetY(); nY++) { - bFound = false; - - for (int nX = VPtiBoundingBoxCorner[1].nGetX(); nX >= 0; nX--) { - if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - m_ulMissingValueBasementCells++; - continue; - } - - // Found a bounding box edge cell - m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(EAST); - - m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); - m_VEdgeCellEdge.push_back(EAST); - - bFound = true; - break; - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter - << ": could not find a bounding box edge cell for grid row " - << nY << endl; - - return RTN_ERR_BOUNDING_BOX; - } - } - - // Bottom (south) edge - for (int nX = VPtiBoundingBoxCorner[2].nGetX(); - nX >= VPtiBoundingBoxCorner[3].nGetX(); nX--) { - bFound = false; - - for (int nY = VPtiBoundingBoxCorner[2].nGetY(); nY >= 0; nY--) { - if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - m_ulMissingValueBasementCells++; - continue; - } - - // Found a bounding box edge cell - m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(SOUTH); - - m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); - m_VEdgeCellEdge.push_back(SOUTH); - - bFound = true; - break; - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream - << m_ulIter - << ": could not find a bounding box edge cell for grid column " - << nX << endl; - - return RTN_ERR_BOUNDING_BOX; - } - } - - // Left (west) edge - for (int nY = VPtiBoundingBoxCorner[3].nGetY(); - nY >= VPtiBoundingBoxCorner[0].nGetY(); nY--) { - for (int nX = VPtiBoundingBoxCorner[3].nGetX(); nX < m_nXGridSize - 1; nX++) - // for (int nX = VPtiBoundingBoxCorner[3].nGetX(); nX < - // VPtiBoundingBoxCorner[3].nGetX(); nX++) - { - if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { - m_ulMissingValueBasementCells++; - continue; - } - - // Found a bounding box edge cell - m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(WEST); - - m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); - m_VEdgeCellEdge.push_back(WEST); - - bFound = true; - break; - } - - if (!bFound) { - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << m_ulIter - << ": could not find a bounding box edge cell for grid row " - << nY << endl; - - return RTN_ERR_BOUNDING_BOX; - } - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! Reads raster GIS datafiles into the RasterGrid array -//=============================================================================================================================== -int CSimulation::nReadRasterGISFile(int const nDataItem, int const nLayer) { - string strGISFile; - string strDriverCode; - string strDriverDesc; - string strProjection; - string strDataType; - - switch (nDataItem) { - case (LANDFORM_RASTER): - // Initial Landform Class GIS data - strGISFile = m_strInitialLandformFile; - break; - - case (INTERVENTION_CLASS_RASTER): - // Intervention class - strGISFile = m_strInterventionClassFile; - break; - - case (INTERVENTION_HEIGHT_RASTER): - // Intervention height - strGISFile = m_strInterventionHeightFile; - break; - - case (SUSP_SED_RASTER): - // Initial Suspended Sediment GIS data - strGISFile = m_strInitialSuspSedimentFile; - break; - - case (FINE_UNCONS_RASTER): - // Initial Unconsolidated Fine Sediment GIS data - strGISFile = m_VstrInitialFineUnconsSedimentFile[nLayer]; - break; - - case (SAND_UNCONS_RASTER): - // Initial Unconsolidated Sand Sediment GIS data - strGISFile = m_VstrInitialSandUnconsSedimentFile[nLayer]; - break; - - case (COARSE_UNCONS_RASTER): - // Initial Unconsolidated Coarse Sediment GIS data - strGISFile = m_VstrInitialCoarseUnconsSedimentFile[nLayer]; - break; - - case (FINE_CONS_RASTER): - // Initial Consolidated Fine Sediment GIS data - strGISFile = m_VstrInitialFineConsSedimentFile[nLayer]; - break; - - case (SAND_CONS_RASTER): - // Initial Consolidated Sand Sediment GIS data - strGISFile = m_VstrInitialSandConsSedimentFile[nLayer]; - break; - - case (COARSE_CONS_RASTER): - // Initial Consolidated Coarse Sediment GIS data - strGISFile = m_VstrInitialCoarseConsSedimentFile[nLayer]; - break; - } - - // Use GDAL to create a dataset object, which then opens the GIS file - GDALDataset *pGDALDataset = - static_cast(GDALOpen(strGISFile.c_str(), GA_ReadOnly)); - - if (NULL == pGDALDataset) { - // Can't open file (note will already have sent GDAL error message to - // stdout) - cerr << ERR << "cannot open " << strGISFile - << " for input: " << CPLGetLastErrorMsg() << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // Opened OK, so get dataset information - strDriverCode = pGDALDataset->GetDriver()->GetDescription(); - strDriverDesc = pGDALDataset->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); - strProjection = pGDALDataset->GetProjectionRef(); - - // Get geotransformation info - double dGeoTransform[6]; - if (CE_Failure == pGDALDataset->GetGeoTransform(dGeoTransform)) { - // Can't get geotransformation (note will already have sent GDAL error - // message to stdout) - cerr << ERR << CPLGetLastErrorMsg() << " in " << strGISFile << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // Now get dataset size, and do some checks - int const nTmpXSize = pGDALDataset->GetRasterXSize(); - if (nTmpXSize != m_nXGridSize) { - // Error: incorrect number of columns specified - cerr << ERR << "different number of columns in " << strGISFile << " (" - << nTmpXSize << ") and " << m_strInitialBasementDEMFile << "(" - << m_nXGridSize << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - int const nTmpYSize = pGDALDataset->GetRasterYSize(); - if (nTmpYSize != m_nYGridSize) { - // Error: incorrect number of rows specified - cerr << ERR << "different number of rows in " << strGISFile << " (" - << nTmpYSize << ") and " << m_strInitialBasementDEMFile << " (" - << m_nYGridSize << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - double dTmp = m_dGeoTransform[0] - (m_dGeoTransform[1] / 2); - if (!bFPIsEqual(dTmp, m_dNorthWestXExtCRS, TOLERANCE)) { - // Error: different min x from DEM file - cerr << ERR << "different min x values in " << strGISFile << " (" << dTmp - << ") and " << m_strInitialBasementDEMFile << " (" - << m_dNorthWestXExtCRS << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - dTmp = m_dGeoTransform[3] - (m_dGeoTransform[5] / 2); - if (!bFPIsEqual(dTmp, m_dNorthWestYExtCRS, TOLERANCE)) { - // Error: different min x from DEM file - cerr << ERR << "different min y values in " << strGISFile << " (" << dTmp - << ") and " << m_strInitialBasementDEMFile << " (" - << m_dNorthWestYExtCRS << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - double const dTmpResX = tAbs(dGeoTransform[1]); - if (!bFPIsEqual(dTmpResX, m_dCellSide, 1e-2)) { - // Error: different cell size in X direction: note that due to rounding - // errors in some GIS packages, must expect some discrepancies - cerr << ERR << "cell size in X direction (" << dTmpResX << ") in " - << strGISFile << " differs from cell size in of basement DEM (" - << m_dCellSide << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - double const dTmpResY = tAbs(dGeoTransform[5]); - if (!bFPIsEqual(dTmpResY, m_dCellSide, 1e-2)) { - // Error: different cell size in Y direction: note that due to rounding - // errors in some GIS packages, must expect some discrepancies - cerr << ERR << "cell size in Y direction (" << dTmpResY << ") in " - << strGISFile << " differs from cell size of basement DEM (" - << m_dCellSide << ")" << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // Now get GDAL raster band information - GDALRasterBand *pGDALBand = pGDALDataset->GetRasterBand( - 1); // TODO 028 Give a message if there are several bands - int nBlockXSize = 0, nBlockYSize = 0; - pGDALBand->GetBlockSize(&nBlockXSize, &nBlockYSize); - strDataType = GDALGetDataTypeName(pGDALBand->GetRasterDataType()); - - switch (nDataItem) { - case (LANDFORM_RASTER): - // Initial Landform Class GIS data - m_strGDALLDriverCode = strDriverCode; - m_strGDALLDriverDesc = strDriverDesc; - m_strGDALLProjection = strProjection; - m_strGDALLDataType = strDataType; - break; - - case (INTERVENTION_CLASS_RASTER): - // Intervention class - m_strGDALICDriverCode = strDriverCode; - m_strGDALICDriverDesc = strDriverDesc; - m_strGDALICProjection = strProjection; - m_strGDALICDataType = strDataType; - break; - - case (INTERVENTION_HEIGHT_RASTER): - // Intervention height - m_strGDALIHDriverCode = strDriverCode; - m_strGDALIHDriverDesc = strDriverDesc; - m_strGDALIHProjection = strProjection; - m_strGDALIHDataType = strDataType; - break; - - case (SUSP_SED_RASTER): - // Initial Suspended Sediment GIS data - m_strGDALISSDriverCode = strDriverCode; - m_strGDALISSDriverDesc = strDriverDesc; - m_strGDALISSProjection = strProjection; - m_strGDALISSDataType = strDataType; - break; - - case (FINE_UNCONS_RASTER): - // Initial Unconsolidated Fine Sediment GIS data - m_VstrGDALIUFDriverCode[nLayer] = strDriverCode; - m_VstrGDALIUFDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALIUFProjection[nLayer] = strProjection; - m_VstrGDALIUFDataType[nLayer] = strDataType; - break; - - case (SAND_UNCONS_RASTER): - // Initial Unconsolidated Sand Sediment GIS data - m_VstrGDALIUSDriverCode[nLayer] = strDriverCode; - m_VstrGDALIUSDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALIUSProjection[nLayer] = strProjection; - m_VstrGDALIUSDataType[nLayer] = strDataType; - break; - - case (COARSE_UNCONS_RASTER): - // Initial Unconsolidated Coarse Sediment GIS data - m_VstrGDALIUCDriverCode[nLayer] = strDriverCode; - m_VstrGDALIUCDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALIUCProjection[nLayer] = strProjection; - m_VstrGDALIUCDataType[nLayer] = strDataType; - break; - - case (FINE_CONS_RASTER): - // Initial Consolidated Fine Sediment GIS data - m_VstrGDALICFDriverCode[nLayer] = strDriverCode; - m_VstrGDALICFDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALICFProjection[nLayer] = strProjection; - m_VstrGDALICFDataType[nLayer] = strDataType; - break; - - case (SAND_CONS_RASTER): - // Initial Consolidated Sand Sediment GIS data - m_VstrGDALICSDriverCode[nLayer] = strDriverCode; - m_VstrGDALICSDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALICSProjection[nLayer] = strProjection; - m_VstrGDALICSDataType[nLayer] = strDataType; - break; - - case (COARSE_CONS_RASTER): - // Initial Consolidated Coarse Sediment GIS data - m_VstrGDALICCDriverCode[nLayer] = strDriverCode; - m_VstrGDALICCDriverDesc[nLayer] = strDriverDesc; - m_VstrGDALICCProjection[nLayer] = strProjection; - m_VstrGDALICCDataType[nLayer] = strDataType; - break; - } - - // If present, get the missing value setting - string const strTmp = strToLower(&strDataType); - if (strTmp.find("int") != string::npos) { - // This is an integer layer - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to - // fail silently, if it fails - m_nGISMissingValue = static_cast( - pGDALBand->GetNoDataValue()); // Note will fail for some formats - CPLPopErrorHandler(); - - if (m_nGISMissingValue != m_nMissingValue) { - cerr - << " " << NOTE << "NODATA value in " << strGISFile << " is " - << m_nGISMissingValue - << "\n instead using CoatalME's default integer NODATA value " - << m_nMissingValue << endl; - } - } else { - // This is a floating point layer - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to - // fail silently, if it fails - m_dGISMissingValue = - pGDALBand->GetNoDataValue(); // Note will fail for some formats - CPLPopErrorHandler(); - - if (!bFPIsEqual(m_dGISMissingValue, m_dMissingValue, TOLERANCE)) { - cerr << " " << NOTE << "NODATA value in " << strGISFile << " is " - << m_dGISMissingValue - << "\n instead using CoastalME's default floating-point " - "NODATA value " - << m_dMissingValue << endl; - } - } - - // Allocate memory for a 1D array, to hold the scan line for GDAL - double *pdScanline = new double[m_nXGridSize]; - if (NULL == pdScanline) { - // Error, can't allocate memory - cerr << ERR << "cannot allocate memory for " << m_nXGridSize - << " x 1D array" << endl; - return (RTN_ERR_MEMALLOC); - } - - // Now read in the data - int nMissing = 0; - - for (int nY = 0; nY < m_nYGridSize; nY++) { - // Read scanline - if (CE_Failure == pGDALBand->RasterIO(GF_Read, 0, nY, m_nXGridSize, 1, - pdScanline, m_nXGridSize, 1, - GDT_Float64, 0, 0, NULL)) { - // Error while reading scanline - cerr << ERR << CPLGetLastErrorMsg() << " in " << strGISFile << endl; - return (RTN_ERR_RASTER_FILE_READ); - } - - // All OK, so read scanline into cells (including any missing values) - for (int nX = 0; nX < m_nXGridSize; nX++) { - int nTmp; - - switch (nDataItem) { - case (LANDFORM_RASTER): - // Initial Landform Class GIS data, is integer TODO 030 Do we also need - // a landform sub-category input? - nTmp = static_cast(pdScanline[nX]); - - if ((isnan(nTmp)) || (nTmp == m_nGISMissingValue)) { - nTmp = m_nMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->SetLFCategory(nTmp); - break; - - case (INTERVENTION_CLASS_RASTER): - // Intervention class, is integer. If not an intervention, show INT_NODATA - nTmp = static_cast(pdScanline[nX]); - - if ((isnan(nTmp)) || (nTmp == m_nGISMissingValue)) - { - nTmp = m_nMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->SetLFCategory(nTmp); - break; - - case (INTERVENTION_HEIGHT_RASTER): - // Intervention height - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY].SetInterventionHeight(dTmp); - break; - - case (SUSP_SED_RASTER): - // Initial Suspended Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY].SetSuspendedSediment(dTmp); - break; - - case (FINE_UNCONS_RASTER): - // Initial Unconsolidated Fine Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetUnconsolidatedSediment() - ->SetFineDepth(dTmp); - break; - - case (SAND_UNCONS_RASTER): - // Initial Unconsolidated Sand Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetUnconsolidatedSediment() - ->SetSandDepth(dTmp); - break; - - case (COARSE_UNCONS_RASTER): - // Initial Unconsolidated Coarse Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetUnconsolidatedSediment() - ->SetCoarseDepth(dTmp); - break; - - case (FINE_CONS_RASTER): - // Initial Consolidated Fine Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetConsolidatedSediment() - ->SetFineDepth(dTmp); - break; - - case (SAND_CONS_RASTER): - // Initial Consolidated Sand Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetConsolidatedSediment() - ->SetSandDepth(dTmp); - break; - - case (COARSE_CONS_RASTER): - // Initial Consolidated Coarse Sediment GIS data - dTmp = pdScanline[nX]; - - if ((isnan(dTmp)) || - (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { - dTmp = m_dMissingValue; - nMissing++; - } - - m_pRasterGrid->m_Cell[nX][nY] - .pGetLayerAboveBasement(nLayer) - ->pGetConsolidatedSediment() - ->SetCoarseDepth(dTmp); - break; - } - } - } - - // Finished, so get rid of dataset object - GDALClose(pGDALDataset); - - // Get rid of memory allocated to this array - delete[] pdScanline; - - if (nMissing > 0) { - cerr << WARN << nMissing << " missing values in " << strGISFile << endl; - LogStream << WARN << nMissing << " missing values in " << strGISFile - << endl; - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! Writes GIS raster files using GDAL, using data from the RasterGrid array -//=============================================================================================================================== -bool CSimulation::bWriteRasterGISFile(int const nDataItem, - string const *strPlotTitle, - int const nLayer, double const dElev) { - bool bIsInteger = false; - bool bIsUnsignedLong = false; - - // Begin constructing the file name for this save - string strFilePathName(m_strOutPath); - string strLayer = "_layer_"; - - stringstream ststrTmp; - - strLayer.append(to_string(nLayer + 1)); - - switch (nDataItem) { - case (RASTER_PLOT_BASEMENT_ELEVATION): - strFilePathName.append(RASTER_BASEMENT_ELEVATION_NAME); - break; - - case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): - strFilePathName.append(RASTER_SEDIMENT_TOP_ELEVATION_NAME); - break; - - case (RASTER_PLOT_TOP_ELEV_INC_SEA): - strFilePathName.append(RASTER_TOP_ELEVATION_INC_SEA_NAME); - break; - - case (RASTER_PLOT_TALUS): - strFilePathName.append(RASTER_TALUS_NAME); - break; - - case (RASTER_PLOT_CONS_SED_SLOPE): - strFilePathName.append(RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_NAME); - break; - - case (RASTER_PLOT_SLOPE_FOR_CLIFF_TOE): - strFilePathName.append(RASTER_SLOPE_FOR_CLIFF_TOE_NAME); - break; - - case (RASTER_PLOT_CLIFF_TOE): - strFilePathName.append(RASTER_CLIFF_TOE_NAME); - break; - - case (RASTER_PLOT_SEA_DEPTH): - strFilePathName.append(RASTER_SEA_DEPTH_NAME); - break; - - case (RASTER_PLOT_AVG_SEA_DEPTH): - strFilePathName.append(RASTER_AVG_SEA_DEPTH_NAME); - break; - - case (RASTER_PLOT_WAVE_HEIGHT): - strFilePathName.append(RASTER_WAVE_HEIGHT_NAME); - break; - - case (RASTER_PLOT_AVG_WAVE_HEIGHT): - strFilePathName.append(RASTER_AVG_WAVE_HEIGHT_NAME); - break; - - case (RASTER_PLOT_WAVE_ORIENTATION): - strFilePathName.append(RASTER_WAVE_ORIENTATION_NAME); - break; - - case (RASTER_PLOT_AVG_WAVE_ORIENTATION): - strFilePathName.append(RASTER_AVG_WAVE_ORIENTATION_NAME); - break; - - case (RASTER_PLOT_BEACH_PROTECTION): - strFilePathName.append(RASTER_BEACH_PROTECTION_NAME); - break; - - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): - strFilePathName.append(RASTER_POTENTIAL_PLATFORM_EROSION_NAME); - break; - - case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): - strFilePathName.append(RASTER_ACTUAL_PLATFORM_EROSION_NAME); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): - strFilePathName.append(RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_NAME); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): - strFilePathName.append(RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_NAME); - break; - - case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): - strFilePathName.append(RASTER_POTENTIAL_BEACH_EROSION_NAME); - break; - - case (RASTER_PLOT_ACTUAL_BEACH_EROSION): - strFilePathName.append(RASTER_ACTUAL_BEACH_EROSION_NAME); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): - strFilePathName.append(RASTER_TOTAL_POTENTIAL_BEACH_EROSION_NAME); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): - strFilePathName.append(RASTER_TOTAL_ACTUAL_BEACH_EROSION_NAME); - break; - - case (RASTER_PLOT_BEACH_DEPOSITION): - strFilePathName.append(RASTER_BEACH_DEPOSITION_NAME); - break; - - case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): - strFilePathName.append(RASTER_TOTAL_BEACH_DEPOSITION_NAME); - break; - - case (RASTER_PLOT_SUSPENDED_SEDIMENT): - strFilePathName.append(RASTER_SUSP_SED_NAME); - break; - - case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): - strFilePathName.append(RASTER_AVG_SUSP_SED_NAME); - break; - - case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_FINE_UNCONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_SAND_UNCONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_COARSE_UNCONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_FINE_CONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_SAND_CONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): - strFilePathName.append(RASTER_COARSE_CONS_NAME); - strFilePathName.append(strLayer); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_FINE_NAME); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_SAND_NAME); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_COARSE_NAME); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): - strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_NAME); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): - strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_NAME); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): - strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_NAME); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): - strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): - strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): - strFilePathName.append(RASTER_CLIFF_COLLAPSE_TIMESTEP_NAME); - break; - - case (RASTER_PLOT_CLIFF_NOTCH_ALL): - strFilePathName.append(RASTER_CLIFF_NOTCH_ALL_NAME); - break; - - case (RASTER_PLOT_INTERVENTION_HEIGHT): - strFilePathName.append(RASTER_INTERVENTION_HEIGHT_NAME); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): - strFilePathName.append(RASTER_DEEP_WATER_WAVE_ORIENTATION_NAME); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): - strFilePathName.append(RASTER_DEEP_WATER_WAVE_HEIGHT_NAME); - break; - - case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): - strFilePathName.append(RASTER_POLYGON_GAIN_OR_LOSS_NAME); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): - strFilePathName.append(RASTER_WAVE_PERIOD_NAME); - break; - - case (RASTER_PLOT_SEDIMENT_INPUT): - strFilePathName.append(RASTER_SEDIMENT_INPUT_EVENT_NAME); - break; - - case (RASTER_PLOT_BEACH_MASK): - bIsInteger = true; - strFilePathName.append(RASTER_BEACH_MASK_NAME); - break; - - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): - bIsInteger = true; - strFilePathName.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_NAME); - break; - - case (RASTER_PLOT_INUNDATION_MASK): - bIsInteger = true; - strFilePathName.append(RASTER_INUNDATION_MASK_NAME); - break; - - case (RASTER_PLOT_SLICE): - bIsInteger = true; - ststrTmp.str(""); - ststrTmp.clear(); - - // TODO 031 Get working for multiple slices - strFilePathName.append(RASTER_SLICE_NAME); - ststrTmp << "_" << dElev << "_"; - strFilePathName.append(ststrTmp.str()); - break; - - case (RASTER_PLOT_LANDFORM): - bIsInteger = true; - strFilePathName.append(RASTER_LANDFORM_NAME); - break; - - case (RASTER_PLOT_INTERVENTION_CLASS): - bIsInteger = true; - strFilePathName.append(RASTER_INTERVENTION_CLASS_NAME); - break; - - case (RASTER_PLOT_COAST): - bIsInteger = true; - strFilePathName.append(RASTER_COAST_NAME); - break; - - case (RASTER_PLOT_NORMAL_PROFILE): - bIsInteger = true; - strFilePathName.append(RASTER_COAST_NORMAL_NAME); - break; - - case (RASTER_PLOT_ACTIVE_ZONE): - bIsInteger = true; - strFilePathName.append(RASTER_ACTIVE_ZONE_NAME); - break; - - case (RASTER_PLOT_POLYGON): - bIsInteger = true; - strFilePathName.append(RASTER_POLYGON_NAME); - break; - - case (RASTER_PLOT_SHADOW_ZONE): - bIsInteger = true; - strFilePathName.append(RASTER_SHADOW_ZONE_NAME); - break; - - case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): - bIsInteger = true; - strFilePathName.append(RASTER_SHADOW_DOWNDRIFT_ZONE_NAME); - break; - - case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): - bIsInteger = true; - strFilePathName.append(RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_NAME); - break; - - case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): - bIsInteger = true; - strFilePathName.append(RASTER_SETUP_SURGE_FLOOD_MASK_NAME); - break; - - case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): - bIsInteger = true; - strFilePathName.append(RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_NAME); - break; - - case (RASTER_PLOT_WAVE_FLOOD_LINE): - bIsInteger = true; - strFilePathName.append(RASTER_WAVE_FLOOD_LINE_NAME); - break; - } - - // Append the 'save number' to the filename, and prepend zeros to the save - // number - ststrTmp.str(""); - ststrTmp.clear(); - - strFilePathName.append("_"); - - if (m_bGISSaveDigitsSequential) { - // Save number is m_bGISSaveDigitsSequential - ststrTmp << FillToWidth('0', m_nGISMaxSaveDigits) << m_nGISSave; - } else { - // Save number is iteration - ststrTmp << FillToWidth('0', m_nGISMaxSaveDigits) << m_ulIter; - } - - strFilePathName.append(ststrTmp.str()); - - // Finally, maybe append the extension - if (!m_strGDALRasterOutputDriverExtension.empty()) { - strFilePathName.append("."); - strFilePathName.append(m_strGDALRasterOutputDriverExtension); - } - - // TODO 065 Used to try to debug floating point exception in pDriver->Create() - // below CPLSetConfigOption("CPL_DEBUG", "ON"); - // CPLSetConfigOption("GDAL_NUM_THREADS", "1"); - - GDALDriver *pDriver; - GDALDataset *pDataSet; - - if (m_bGDALCanCreate) { - // The user-requested raster driver supports the Create() method - pDriver = GetGDALDriverManager()->GetDriverByName( - m_strRasterGISOutFormat.c_str()); - - if (bIsInteger) { - pDataSet = - pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, - 1, GDT_Int16, m_papszGDALRasterOptions); - } else if (bIsUnsignedLong) { - pDataSet = - pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, - 1, GDT_UInt32, m_papszGDALRasterOptions); - - } else if (m_strRasterGISOutFormat == "gpkg") { - // TODO 065 Floating point exception here - pDataSet = - pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, - 1, GDT_Byte, m_papszGDALRasterOptions); - } else { - pDataSet = pDriver->Create(strFilePathName.c_str(), m_nXGridSize, - m_nYGridSize, 1, m_GDALWriteFloatDataType, - m_papszGDALRasterOptions); - } - - if (NULL == pDataSet) { - // Error, couldn't create file - cerr << ERR << "cannot create " << m_strRasterGISOutFormat - << " file named " << strFilePathName << endl; - return false; - } - } - else - { - // The user-requested raster driver does not support the Create() method, so we must first create a memory-file dataset - pDriver = GetGDALDriverManager()->GetDriverByName("MEM"); - pDataSet = pDriver->Create("", m_nXGridSize, m_nYGridSize, 1, m_GDALWriteFloatDataType, NULL); - - if (NULL == pDataSet) - { - // Couldn't create in-memory file dataset - cerr << ERR << "cannot create in-memory file for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl - << CPLGetLastErrorMsg() << endl; - return false; - } - } - - // Set projection info for output dataset (will be same as was read in from basement DEM) - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails - pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); // Will fail for some formats - CPLPopErrorHandler(); - - // Set geotransformation info for output dataset (will be same as was read in from DEM) - if (CE_Failure == pDataSet->SetGeoTransform(m_dGeoTransform)) - LogStream << WARN << "cannot write geotransformation information to " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl << CPLGetLastErrorMsg() << endl; - - // Allocate memory for a 1D array, to hold the floating point raster band data for GDAL - double *pdRaster = new double[m_ulNumCells]; - if (NULL == pdRaster) - { - // Error, can't allocate memory - cerr << ERR << "cannot allocate memory for " << m_ulNumCells << " x 1D floating-point array for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl; - return (RTN_ERR_MEMALLOC); - } - - bool bScaleOutput = false; - double dRangeScale = 0; - double dDataMin = 0; - - if (! m_bGDALCanWriteFloat) - { - double dDataMax = 0; - - // The output file format cannot handle floating-point numbers, so we may need to scale the output - GetRasterOutputMinMax(nDataItem, dDataMin, dDataMax, nLayer, 0); - - double const dDataRange = dDataMax - dDataMin; - double const dWriteRange = static_cast(m_lGDALMaxCanWrite - m_lGDALMinCanWrite); - - if (dDataRange > 0) - dRangeScale = dWriteRange / dDataRange; - - // If we are attempting to write values which are outside this format's allowable range, and the user has set the option, then scale the output - if (((dDataMin < static_cast(m_lGDALMinCanWrite)) || (dDataMax > static_cast(m_lGDALMaxCanWrite))) && m_bScaleRasterOutput) - bScaleOutput = true; - } - - // Fill the array - int n = 0; - int nPoly = 0; - int nPolyCoast = 0; - int nTopLayer = 0; - double dTmp = 0; - - for (int nY = 0; nY < m_nYGridSize; nY++) - { - for (int nX = 0; nX < m_nXGridSize; nX++) - { - switch (nDataItem) - { - case (RASTER_PLOT_BASEMENT_ELEVATION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBasementElev(); - break; - - case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus(); - break; - - case (RASTER_PLOT_TOP_ELEV_INC_SEA): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTopElevIncSea(); - break; - - case (RASTER_PLOT_TALUS): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTalusDepth(); - break; - - case (RASTER_PLOT_CONS_SED_SLOPE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetConsSedSlope(); - break; - - case (RASTER_PLOT_SLOPE_FOR_CLIFF_TOE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSlopeForCliffToe(); - break; - - case (RASTER_PLOT_CLIFF_TOE): - dTmp = static_cast(m_pRasterGrid->m_Cell[nX][nY].bIsCliffToe()); - break; - - case (RASTER_PLOT_SEA_DEPTH): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSeaDepth(); - break; - - case (RASTER_PLOT_AVG_SEA_DEPTH): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSeaDepth() / static_cast(m_ulIter); - break; - - case (RASTER_PLOT_WAVE_HEIGHT): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); - else - dTmp = 0; - break; - - case (RASTER_PLOT_AVG_WAVE_HEIGHT): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveHeight() / static_cast(m_ulIter); - else - dTmp = 0; - break; - - case (RASTER_PLOT_WAVE_ORIENTATION): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); - else - dTmp = 0; - break; - - case (RASTER_PLOT_AVG_WAVE_ORIENTATION): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveAngle() / static_cast(m_ulIter); - else - dTmp = 0; - break; - - case (RASTER_PLOT_BEACH_PROTECTION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachProtectionFactor(); - - if (bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) - dTmp = m_dMissingValue; - else - dTmp = 1 - dTmp; // Output the inverse, seems more intuitive - break; - - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialPlatformErosion(); - break; - - case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualPlatformErosion(); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialPlatformErosion(); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualPlatformErosion(); - break; - - case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialBeachErosion(); - break; - - case (RASTER_PLOT_ACTUAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualBeachErosion(); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialBeachErosion(); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualBeachErosion(); - break; - - case (RASTER_PLOT_BEACH_DEPOSITION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachDeposition(); - break; - - case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotBeachDeposition(); - break; - - case (RASTER_PLOT_SUSPENDED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSuspendedSediment(); - break; - - case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSuspendedSediment() / static_cast(m_ulIter); - break; - - case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetFineDepth(); - break; - - case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetSandDepth(); - break; - - case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetCoarseDepth(); - break; - - case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetFineDepth(); - break; - - case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetSandDepth(); - break; - - case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetCoarseDepth(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionFine(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionSand(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionCoarse(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseFine(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseSand(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseCoarse(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseSandTalusDeposition(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseCoarseTalusDeposition(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSandTalusDeposition(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCoarseTalusDeposition(); - break; - - case (RASTER_PLOT_CLIFF_NOTCH_ALL): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->dGetCliffNotchIncisionDepth(); - break; - -#ifdef _DEBUG - case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): - dTmp = static_cast(m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->ulGetCliffCollapseTimestep()); - bIsUnsignedLong = true; - break; -#endif - - case (RASTER_PLOT_INTERVENTION_HEIGHT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetInterventionHeight(); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle(); - else - dTmp = 0; - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight(); - else - dTmp = 0; - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): - if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWavePeriod(); - else - dTmp = 0; - break; - - case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): - nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); - - if (nPoly == INT_NODATA) - dTmp = m_dMissingValue; - else - { - // Get total volume (all sediment size classes) of change in sediment for this polygon for this timestep (-ve erosion, +ve deposition) - dTmp = m_VCoast[nPolyCoast].pGetPolygon(nPoly)->dGetBeachDepositionAndSuspensionAllUncons() * m_dCellArea; - - // Calculate the rate in m^3 / sec - dTmp /= (m_dTimeStep * 3600); - } - break; - - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): - // cppcheck-suppress assignBoolToFloat - dTmp = m_pRasterGrid->m_Cell[nX][nY].bPotentialPlatformErosion(); - break; - - case (RASTER_PLOT_INUNDATION_MASK): - // cppcheck-suppress assignBoolToFloat - dTmp = m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea(); - break; - - case (RASTER_PLOT_BEACH_MASK): - dTmp = 0; - nTopLayer = m_pRasterGrid->m_Cell[nX][nY].nGetTopNonZeroLayerAboveBasement(); - - if ((nTopLayer == INT_NODATA) || (nTopLayer == NO_NONZERO_THICKNESS_LAYERS)) - break; - - if ((m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nTopLayer)->dGetAllUnconsDepth() > 0) && (m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() > m_dThisIterSWL)) - dTmp = 1; - - break; - - case (RASTER_PLOT_SLICE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetLayerAtElev(dElev); - break; - - case (RASTER_PLOT_LANDFORM): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); - bIsInteger = true; - break; - - case (RASTER_PLOT_INTERVENTION_CLASS): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetInterventionClass(); - bIsInteger = true; - break; - - case (RASTER_PLOT_COAST): - dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline() ? 1 : 0); - break; - - case (RASTER_PLOT_NORMAL_PROFILE): - // dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsProfile() ? 1 : 0); - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); - bIsInteger = true; - break; - - case (RASTER_PLOT_ACTIVE_ZONE): - dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsInActiveZone() ? 1 : 0); - break; - - case (RASTER_PLOT_POLYGON): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - bIsInteger = true; - break; - - case (RASTER_PLOT_SHADOW_ZONE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber(); - bIsInteger = true; - break; - - case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); - bIsInteger = true; - break; - - case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): - nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); - bIsInteger = true; - - if (nPoly == INT_NODATA) - dTmp = m_nMissingValue; - else - { - if (m_VCoast[nPolyCoast].pGetPolygon(nPoly)->bDownCoastThisIter()) - dTmp = 1; - else - dTmp = 0; - } - break; - - case (RASTER_PLOT_SEDIMENT_INPUT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nTopLayer)->pGetUnconsolidatedSediment()->dGetTotAllSedimentInputDepth(); - break; - - case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): - dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodBySetupSurge() ? 1 : 0); - break; - - case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): - dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodBySetupSurgeRunup() ? 1 : 0); - break; - - case (RASTER_PLOT_WAVE_FLOOD_LINE): - dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodline() ? 1 : 0); - break; - } - - // If necessary, scale this value - if (bScaleOutput) - { - if (bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) - dTmp = 0; // TODO 032 Improve this - else - dTmp = dRound(static_cast(m_lGDALMinCanWrite) + (dRangeScale * (dTmp - dDataMin))); - } - - // Write this value to the array - pdRaster[n++] = dTmp; - } - } - - // Create a single raster band - GDALRasterBand *pBand = pDataSet->GetRasterBand(1); - - // And fill it with the NODATA value - if (bIsInteger) - pBand->Fill(m_nMissingValue); - else if (bIsUnsignedLong) - pBand->Fill(static_cast(m_ulMissingValue)); - else - pBand->Fill(m_dMissingValue); - - // Set value units for this band - string strUnits; - - switch (nDataItem) - { - case (RASTER_PLOT_ACTUAL_BEACH_EROSION): - case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): - case (RASTER_PLOT_AVG_SEA_DEPTH): - case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): - case (RASTER_PLOT_AVG_WAVE_HEIGHT): - case (RASTER_PLOT_BASEMENT_ELEVATION): - case (RASTER_PLOT_BEACH_DEPOSITION): - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): - case (RASTER_PLOT_CLIFF_NOTCH_ALL): - case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): - case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_INTERVENTION_HEIGHT): - case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): - case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): - case (RASTER_PLOT_SEA_DEPTH): - case (RASTER_PLOT_SEDIMENT_INPUT): - case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): - case (RASTER_PLOT_SUSPENDED_SEDIMENT): - case (RASTER_PLOT_TALUS): - case (RASTER_PLOT_TOP_ELEV_INC_SEA): - case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): - case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): - case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): - case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): - case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): - case (RASTER_PLOT_WAVE_HEIGHT): - strUnits = "m"; - break; - - case (RASTER_PLOT_CONS_SED_SLOPE): - strUnits = "m/m"; - break; - - strUnits = "degrees"; - case (RASTER_PLOT_AVG_WAVE_ORIENTATION): - case (RASTER_PLOT_WAVE_ORIENTATION): - break; - - case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): - strUnits = "cumecs"; - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): - strUnits = "secs"; - break; - - strUnits = "none"; - case (RASTER_PLOT_ACTIVE_ZONE): - case (RASTER_PLOT_BEACH_MASK): -#ifdef _DEBUG - case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): -#endif - case (RASTER_PLOT_COAST): - case (RASTER_PLOT_INTERVENTION_CLASS): - case (RASTER_PLOT_INUNDATION_MASK): - case (RASTER_PLOT_LANDFORM): - case (RASTER_PLOT_NORMAL_PROFILE): - case (RASTER_PLOT_POLYGON): - case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): - case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): - case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): - case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): - case (RASTER_PLOT_SHADOW_ZONE): - case (RASTER_PLOT_SLICE): - case (RASTER_PLOT_WAVE_FLOOD_LINE): - break; - } - - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails - pBand->SetUnitType(strUnits.c_str()); // Not supported for some GIS formats - CPLPopErrorHandler(); - - // Tell the output dataset about NODATA (missing values) - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails - - if (bIsInteger) - pBand->SetNoDataValue(m_nMissingValue); // Will fail for some formats - if (bIsUnsignedLong) - pBand->SetNoDataValueAsUInt64(m_ulMissingValue); // Will fail for some formats - else - pBand->SetNoDataValue(m_dMissingValue); // Will fail for some formats - - CPLPopErrorHandler(); - - // Construct the description - string strDesc(*strPlotTitle); - - if (nDataItem == RASTER_PLOT_SLICE) - { - ststrTmp.clear(); - ststrTmp << dElev << "m, "; - strDesc.append(ststrTmp.str()); - } - - strDesc.append(" at "); - strDesc.append(strDispTime(m_dSimElapsed, false, false)); - - // Set the GDAL description - pBand->SetDescription(strDesc.c_str()); - - // Set raster category names - char **papszCategoryNames = NULL; - - switch (nDataItem) - { - case (RASTER_PLOT_SLICE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Basement"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 0"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 1"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 2"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 3"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 4"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 5"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 6"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 7"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 8"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 9"); - break; - - case (RASTER_PLOT_LANDFORM): - papszCategoryNames = CSLAddString(papszCategoryNames, "None"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Hinterland"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Sea"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Cliff"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Drift"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Intervention"); - - papszCategoryNames = CSLAddString(papszCategoryNames, "Cliff on Coastline"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Inland Cliff"); - - papszCategoryNames = CSLAddString(papszCategoryNames, "Mixed Drift"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Talus"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Beach"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Dunes"); - break; - - case (RASTER_PLOT_INTERVENTION_CLASS): - papszCategoryNames = CSLAddString(papszCategoryNames, "None"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Structural"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Non-Structural"); - break; - - case (RASTER_PLOT_COAST): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not coastline"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Coastline"); - break; - - case (RASTER_PLOT_NORMAL_PROFILE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not coastline-normal profile"); - papszCategoryNames = CSLAddString(papszCategoryNames, "Coastline-normal profile"); - break; - - case (RASTER_PLOT_ACTIVE_ZONE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not in active zone"); - papszCategoryNames = CSLAddString(papszCategoryNames, "In active zone"); - break; - - case (RASTER_PLOT_POLYGON): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not polygon"); - papszCategoryNames = CSLAddString(papszCategoryNames, "In polygon"); - break; - - case (RASTER_PLOT_SHADOW_ZONE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not in shadow zone"); - papszCategoryNames = CSLAddString(papszCategoryNames, "In shadow zone"); - break; - - case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Not in shadow downdrift zone"); - papszCategoryNames = CSLAddString(papszCategoryNames, "In shadow downdrift zone"); - break; - - case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): - papszCategoryNames = CSLAddString(papszCategoryNames, "Updrift movement of unconsolidated sediment "); - papszCategoryNames = CSLAddString(papszCategoryNames, "Downdrift movement of unconsolidated sediment"); - break; - - case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): - papszCategoryNames = CSLAddString(papszCategoryNames, "Inundated by swl setup and surge "); - papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl setup and surge"); - break; - - case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): - papszCategoryNames = CSLAddString(papszCategoryNames, "Inundated by swl setup, surge and runup "); - papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl setup, surge and runup"); - break; - - case (RASTER_PLOT_WAVE_FLOOD_LINE): - papszCategoryNames = CSLAddString(papszCategoryNames, "Intersection line of inundation "); - papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl waves and runup"); - break; - } - - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails - pBand->SetCategoryNames(papszCategoryNames); // Not supported for some GIS formats - CPLPopErrorHandler(); - - // Now write the data with optimized I/O - // Enable multi-threaded compression for faster writing - CPLSetThreadLocalConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); - - if (CE_Failure == pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL)) - { - // Write error, better error message - cerr << ERR << "cannot write data for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl - << CPLGetLastErrorMsg() << endl; - delete[] pdRaster; - return false; - } - - // Calculate statistics for this band - double dMin, dMax, dMean, dStdDev; - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail - // silently, if it fails - pBand->ComputeStatistics(false, &dMin, &dMax, &dMean, &dStdDev, NULL, NULL); - CPLPopErrorHandler(); - - // And then write the statistics - CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail - // silently, if it fails - pBand->SetStatistics(dMin, dMax, dMean, dStdDev); - CPLPopErrorHandler(); - - if (!m_bGDALCanCreate) { - // Since the user-selected raster driver cannot use the Create() method, we - // have been writing to a dataset created by the in-memory driver. So now we - // need to use CreateCopy() to copy this in-memory dataset to a file in the - // user-specified raster driver format - GDALDriver *pOutDriver = GetGDALDriverManager()->GetDriverByName( - m_strRasterGISOutFormat.c_str()); - GDALDataset *pOutDataSet = - pOutDriver->CreateCopy(strFilePathName.c_str(), pDataSet, false, - m_papszGDALRasterOptions, NULL, NULL); - - if (NULL == pOutDataSet) { - // Couldn't create file - cerr << ERR << "cannot create " << m_strRasterGISOutFormat - << " file named " << strFilePathName << endl - << CPLGetLastErrorMsg() << endl; - return false; - } - - // Get rid of this user-selected dataset object - GDALClose(pOutDataSet); - } - - // Get rid of dataset object - GDALClose(pDataSet); - - // Also get rid of memory allocated to this array - delete[] pdRaster; - - return true; -} - -//=============================================================================================================================== -//! Interpolates wave properties from profile points to all cells within polygons -//! -//! ALGORITHM: k-Nearest Neighbor Inverse Distance Weighting (k-NN IDW) -//! -//! This function takes wave height components (X and Y) measured at discrete profile points -//! and interpolates them to a regular grid using a spatial interpolation method. -//! -//! METHOD OVERVIEW: -//! 1. Builds a k-d tree spatial index from input profile point coordinates (X, Y) -//! 2. For each grid cell, finds the k nearest profile points (typically k=12) -//! 3. Calculates interpolated value using inverse distance weighting (IDW) with power=2 -//! 4. Converts X/Y wave components back to magnitude and direction -//! 5. Updates grid cells with interpolated wave properties -//! -//! KEY PARAMETERS (defined in spatial_interpolation.cpp DualSpatialInterpolator): -//! - k_neighbors = 12 Number of nearest points to use for interpolation -//! - power = 2.0 Exponent for inverse distance weighting -//! (higher = more weight to closer points) -//! -//! TUNING GUIDANCE: -//! - Increase k_neighbors (e.g., 15-20) for smoother results with more averaging -//! - Decrease k_neighbors (e.g., 8-10) for results that follow local variations more closely -//! - Increase power (e.g., 3.0-4.0) to emphasize nearby points (sharper transitions) -//! - Decrease power (e.g., 1.0-1.5) for smoother, more gradual transitions -//! -//! IMPORTANT NOTES: -//! - This method does NOT respect transect structure (point 1 vs point 2, etc.) -//! - Treats all input points equally regardless of which transect they belong to -//! - Works well for scattered points but may not preserve transect-aligned features -//! - For transect-aware interpolation, consider bilinear methods instead -//! -//! COASTLINE ORIENTATION: -//! - The coastlines are available in m_VCoast[] vector -//! - Each coast has flux orientation: m_VCoast[i].dGetFluxOrientation(coastpoint) -//! - Each coast has breaking wave angle: m_VCoast[i].dGetBreakingWaveAngle(coastpoint) -//! - These could be used to implement coast-aware interpolation algorithms -//! -//! @param pVTransects Vector of TransectWaveData containing wave data per transect -//! @param pVdDeepWaterX X coordinates of deep water grid edge points -//! @param pVdDeepWaterY Y coordinates of deep water grid edge points -//! @param pVdDeepWaterHeightX X component of wave height at deep water points -//! @param pVdDeepWaterHeightY Y component of wave height at deep water points -//! @return RTN_OK on success, error code otherwise -//=============================================================================================================================== -int CSimulation::nInterpolateWavesToPolygonCells( - vector const *pVTransects, - vector const *pVdDeepWaterX, - vector const *pVdDeepWaterY, - vector const *pVdDeepWaterHeightX, - vector const *pVdDeepWaterHeightY) { - - // ============================================================================ - // STEP 1: Calculate grid dimensions and initialize variables - // ============================================================================ - - int nXSize = 0; - int nYSize = 0; - - // Average values used as fallback when interpolation fails or returns NaN - double dXAvg = 0; - double dYAvg = 0; - - // Calculate bounding box size - nXSize = m_nXMaxBoundingBox - m_nXMinBoundingBox + 1; - nYSize = m_nYMaxBoundingBox - m_nYMinBoundingBox + 1; - int const nGridSize = nXSize * nYSize; - - // Count total points across all transects plus deep water points - unsigned int nPoints = 0; - for (const auto& transect : *pVTransects) { - nPoints += static_cast(transect.VdX.size()); - } - nPoints += static_cast(pVdDeepWaterX->size()); - - // Initialize output arrays (will hold interpolated X and Y wave components) - vector VdOutX(nGridSize, 0); - vector VdOutY(nGridSize, 0); - - // ============================================================================ - // STEP 2: Prepare input data for spatial interpolation - // ============================================================================ - - // Flatten transect data and deep water data into contiguous arrays for the interpolator - std::vector points; - std::vector VdHeightX; - std::vector VdHeightY; - - points.reserve(nPoints); - VdHeightX.reserve(nPoints); - VdHeightY.reserve(nPoints); - - // Add profile/transect points - for (const auto& transect : *pVTransects) { - for (size_t i = 0; i < transect.VdX.size(); i++) { - points.emplace_back(transect.VdX[i], transect.VdY[i]); - VdHeightX.push_back(transect.VdHeightX[i]); - VdHeightY.push_back(transect.VdHeightY[i]); - } - } - - // Add deep water grid edge points - for (size_t i = 0; i < pVdDeepWaterX->size(); i++) { - points.emplace_back((*pVdDeepWaterX)[i], (*pVdDeepWaterY)[i]); - VdHeightX.push_back((*pVdDeepWaterHeightX)[i]); - VdHeightY.push_back((*pVdDeepWaterHeightY)[i]); - } - - // ============================================================================ - // STEP 3: Create spatial interpolator - // ============================================================================ - // - // DualSpatialInterpolator parameters: - // - points: Input point coordinates (from profiles/transects) - // - VdHeightX, VdHeightY: Wave height X and Y components at those points - // - k_neighbors = 12: Use 12 nearest neighbors for interpolation - // ** ADJUST THIS to change smoothness vs local detail ** - // - power = 2.0: Inverse distance weighting power - // ** ADJUST THIS to change influence of nearby vs distant points ** - // - // The interpolator builds a k-d tree for fast nearest neighbor search - // and shares it between X and Y interpolation for efficiency - DualSpatialInterpolator interp(points, VdHeightX, VdHeightY, 6, 2.0); - - // ============================================================================ - // STEP 4: Build query points (grid cells where we want interpolated values) - // ============================================================================ - - std::vector query_points; - query_points.reserve(nGridSize); - for (int nY = m_nYMinBoundingBox; nY <= m_nYMaxBoundingBox; nY++) { - for (int nX = m_nXMinBoundingBox; nX <= m_nXMaxBoundingBox; nX++) { - query_points.emplace_back(static_cast(nX), - static_cast(nY)); - } - } - - // ============================================================================ - // STEP 5: Perform batch interpolation - // ============================================================================ - // - // This does the actual interpolation for all grid points at once - // Uses OpenMP parallelization if available (see spatial_interpolation.cpp) - // Interpolates both X and Y components simultaneously using shared k-d tree - interp.Interpolate(query_points, VdOutX, VdOutY); - - // ============================================================================ - // STEP 6: Validate results and calculate average values for fallback - // ============================================================================ - // - // Check for NaN or unreasonably large values and replace with missing value marker - // Also calculate average of valid values to use as fallback - - int nXValid = 0; - int nYValid = 0; - - // Validate X component - for (unsigned int n = 0; n < VdOutX.size(); n++) { - if (isnan(VdOutX[n])) - VdOutX[n] = m_dMissingValue; - else if (tAbs(VdOutX[n]) > 1e10) // Sanity check for unreasonably large values - VdOutX[n] = m_dMissingValue; - else { - dXAvg += VdOutX[n]; - nXValid++; - } - } - - // Validate Y component - for (unsigned int n = 0; n < VdOutY.size(); n++) { - if (isnan(VdOutY[n])) - VdOutY[n] = m_dMissingValue; - else if (tAbs(VdOutY[n]) > 1e10) // Sanity check for unreasonably large values - VdOutY[n] = m_dMissingValue; - else { - dYAvg += VdOutY[n]; - nYValid++; - } - } - - // Calculate averages (for use as fallback when individual cells have missing values) - if (nXValid > 0) - dXAvg /= nXValid; - if (nYValid > 0) - dYAvg /= nYValid; - - // ============================================================================ - // STEP 7: Update grid cells with interpolated wave properties - // ============================================================================ - // - // Convert X and Y components back to magnitude and direction, - // then update each cell's wave attributes - - int n = 0; - - for (int nY = 0; nY < nYSize; nY++) { - for (int nX = 0; nX < nXSize; nX++) { - int const nActualX = nX + m_nXMinBoundingBox; - int const nActualY = nY + m_nYMinBoundingBox; - - if (m_pRasterGrid->m_Cell[nActualX][nActualY] - .bIsInContiguousSea()) { - // Only update sea cells - - if (m_pRasterGrid->m_Cell[nActualX][nActualY].nGetPolygonID() == - INT_NODATA) { - // -------------------------------------------------------------------- - // Deep water cell (NOT in a polygon) - // -------------------------------------------------------------------- - // Use the cell's pre-assigned deep water wave values - // (these cells are beyond the coastal zone, so don't need interpolation) - - double const dDeepWaterWaveHeight = - m_pRasterGrid->m_Cell[nActualX][nActualY] - .dGetCellDeepWaterWaveHeight(); - m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveHeight( - dDeepWaterWaveHeight); - - double const dDeepWaterWaveAngle = - m_pRasterGrid->m_Cell[nActualX][nActualY] - .dGetCellDeepWaterWaveAngle(); - m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveAngle( - dDeepWaterWaveAngle); - } else { - // -------------------------------------------------------------------- - // Coastal zone cell (IN a polygon) - // -------------------------------------------------------------------- - // Use the interpolated wave values calculated above - - double dWaveHeightX; - double dWaveHeightY; - - // Get interpolated X component (use average as fallback if missing/invalid) - if ((isnan(VdOutX[n])) || - (bFPIsEqual(VdOutX[n], m_dMissingValue, TOLERANCE))) - dWaveHeightX = dXAvg; - else - dWaveHeightX = VdOutX[n]; - - // Get interpolated Y component (use average as fallback if missing/invalid) - if ((isnan(VdOutY[n])) || - (bFPIsEqual(VdOutY[n], m_dMissingValue, TOLERANCE))) - dWaveHeightY = dYAvg; - else - dWaveHeightY = VdOutY[n]; - - // Convert X/Y components to magnitude and direction - double const dWaveHeight = sqrt((dWaveHeightX * dWaveHeightX) + - (dWaveHeightY * dWaveHeightY)); - double const dWaveDir = - atan2(dWaveHeightX, dWaveHeightY) * (180 / PI); - - // Update the cell's wave attributes - m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveHeight( - dWaveHeight); - m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveAngle( - dKeepWithin360(dWaveDir)); - - // Calculate wave height-to-depth ratio and update active zone status - // (active zone = where waves are breaking or near-breaking) - double const dSeaDepth = - m_pRasterGrid->m_Cell[nActualX][nActualY].dGetSeaDepth(); - - if ((dWaveHeight / dSeaDepth) >= - m_dBreakingWaveHeightDepthRatio) - m_pRasterGrid->m_Cell[nActualX][nActualY].SetInActiveZone( - true); - - // LogStream << " nX = " << nX << " nY = " << nY << " [" << - // nActualX - // << "][" << nActualY << "] waveheight = " << dWaveHeight << " - // dWaveDir = " << dWaveDir << " dKeepWithin360(dWaveDir) = " << - // dKeepWithin360(dWaveDir) << endl; - } - } - - // Increment with safety check - n++; - n = tMin(n, static_cast(VdOutX.size() - 1)); - } - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! If the user supplies multiple deep water wave height and angle values, -//! this routine interplates these to all cells (including dry land cells) -//=============================================================================================================================== -int CSimulation::nInterpolateAllDeepWaterWaveValues(void) { - // Interpolate deep water height and orientation from multiple - // user-supplied values - unsigned int const nUserPoints = - static_cast(m_VdDeepWaterWaveStationX.size()); - - // Performance optimization: Enable GDAL threading for interpolation - CPLSetThreadLocalConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); - - // Call GDALGridCreate() with the GGA_InverseDistanceToAPower - // interpolation algorithm. It has following parameters: radius1 is the - // first radius (X axis if rotation angle is 0) of the search ellipse, - // set this to zero (the default) to use the whole point array; radius2 - // is the second radius (Y axis if rotation angle is 0) of the search - // ellipse, again set this parameter to zero (the default) to use the - // whole point array; angle is the angle of the search ellipse rotation - // in degrees (counter clockwise, default 0.0); nodata is the NODATA - // marker to fill empty points (default 0.0) TODO 086 - GDALGridInverseDistanceToAPowerOptions *pOptions = - new GDALGridInverseDistanceToAPowerOptions(); - pOptions->dfAngle = 0; - pOptions->dfAnisotropyAngle = 0; - pOptions->dfAnisotropyRatio = 0; - pOptions->dfPower = 2; // Reduced from 3 to 2 for faster computation - pOptions->dfSmoothing = - 50; // Reduced from 100 to 50 for faster computation - pOptions->dfRadius1 = 0; - pOptions->dfRadius2 = 0; - pOptions->nMaxPoints = - 12; // Limit points for faster computation (was 0 = unlimited) - pOptions->nMinPoints = 3; // Minimum points needed for interpolation - pOptions->dfNoDataValue = m_nMissingValue; - - // CPLSetConfigOption("CPL_DEBUG", "ON"); - // CPLSetConfigOption("GDAL_NUM_THREADS", "1"); - - // OK, now create a gridded version of wave height: first create the - // GDAL context TODO 086 GDALGridContext* pContext = - // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, - // nUserPoints, &m_VdDeepWaterWaveStationX[0], - // &m_VdDeepWaterWaveStationY[0], - // &m_VdThisIterDeepWaterWaveStationHeight[0], true); - GDALGridContext *pContext = GDALGridContextCreate( - GGA_InverseDistanceToAPower, pOptions, nUserPoints, - m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), - m_VdThisIterDeepWaterWaveStationHeight.data(), true); - - if (pContext == NULL) { - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Now process the context - double *dHeightOut = new double[m_ulNumCells]; - int nRet = GDALGridContextProcess( - pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, - m_nYGridSize, GDT_Float64, dHeightOut, NULL, NULL); - - if (nRet == CE_Failure) { - delete[] dHeightOut; - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Get rid of the context - GDALGridContextFree(pContext); - - // Next create a gridded version of wave orientation: first create the - // GDAL context pContext = - // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, - // nUserPoints, &(m_VdDeepWaterWaveStationX[0]), - // &(m_VdDeepWaterWaveStationY[0]), - // (&m_VdThisIterDeepWaterWaveStationAngle[0]), true); - pContext = GDALGridContextCreate( - GGA_InverseDistanceToAPower, pOptions, nUserPoints, - m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), - m_VdThisIterDeepWaterWaveStationAngle.data(), true); - - if (pContext == NULL) { - delete[] dHeightOut; - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Now process the context TODO 086 - double *dAngleOut = new double[m_ulNumCells]; - nRet = GDALGridContextProcess( - pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, - m_nYGridSize, GDT_Float64, dAngleOut, NULL, NULL); - - if (nRet == CE_Failure) { - delete[] dHeightOut; - delete[] dAngleOut; - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Get rid of the context - GDALGridContextFree(pContext); - - // OK, now create a gridded version of wave period: first create the - // GDAL context pContext = - // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, - // nUserPoints, &m_VdDeepWaterWaveStationX[0], - // &m_VdDeepWaterWaveStationY[0], - // &m_VdThisIterDeepWaterWaveStationPeriod[0], true); - pContext = GDALGridContextCreate( - GGA_InverseDistanceToAPower, pOptions, nUserPoints, - m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), - m_VdThisIterDeepWaterWaveStationPeriod.data(), true); - - if (pContext == NULL) { - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Now process the context TODO 086 - double *dPeriopdOut = new double[m_ulNumCells]; - nRet = GDALGridContextProcess( - pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, - m_nYGridSize, GDT_Float64, dPeriopdOut, NULL, NULL); - - if (nRet == CE_Failure) { - delete[] dPeriopdOut; - delete pOptions; - return RTN_ERR_GRIDCREATE; - } - - // Get rid of the context - GDALGridContextFree(pContext); - - // The output from GDALGridCreate() is in dHeightOut, dAngleOut and - // dPeriopdOut but must be reversed - vector VdHeight; - vector VdAngle; - vector VdPeriod; - - int n = 0; - int nValidHeight = 0; - int nValidAngle = 0; - int nValidPeriod = 0; - - double dAvgHeight = 0; - double dAvgAngle = 0; - double dAvgPeriod = 0; - - for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { - for (int nX = 0; nX < m_nXGridSize; nX++) { - if (isfinite(dHeightOut[n])) { - VdHeight.push_back(dHeightOut[n]); - - dAvgHeight += dHeightOut[n]; - nValidHeight++; - } - - else { - VdHeight.push_back(m_dMissingValue); - } - - if (isfinite(dAngleOut[n])) { - VdAngle.push_back(dAngleOut[n]); - - dAvgAngle += dAngleOut[n]; - nValidAngle++; - } - - else { - VdAngle.push_back(m_dMissingValue); - } - - if (isfinite(dPeriopdOut[n])) { - VdPeriod.push_back(dPeriopdOut[n]); - - dAvgPeriod += dPeriopdOut[n]; - nValidPeriod++; - } - - else { - VdPeriod.push_back(m_dMissingValue); - } - - // LogStream << " nX = " << nX << " nY = " << nY << " n = " << n << - // " dHeightOut[n] = " << dHeightOut[n] << " dAngleOut[n] = " << - // dAngleOut[n] << endl; - n++; - } - } - - // Calculate averages - dAvgHeight /= nValidHeight; - dAvgAngle /= nValidAngle; - dAvgPeriod /= nValidPeriod; - - // Tidy - delete pOptions; - delete[] dHeightOut; - delete[] dAngleOut; - delete[] dPeriopdOut; - - // Now update all raster cells - n = 0; - - for (int nY = 0; nY < m_nYGridSize; nY++) { - for (int nX = 0; nX < m_nXGridSize; nX++) { - if (bFPIsEqual(VdHeight[n], m_dMissingValue, TOLERANCE)) - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveHeight( - dAvgHeight); - - else - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveHeight( - VdHeight[n]); - - if (bFPIsEqual(VdAngle[n], m_dMissingValue, TOLERANCE)) - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveAngle( - dAvgAngle); - - else - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveAngle( - VdAngle[n]); - - if (bFPIsEqual(VdPeriod[n], m_dMissingValue, TOLERANCE)) - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWavePeriod( - dAvgPeriod); - - else - m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWavePeriod( - VdPeriod[n]); - - // LogStream << " [" << nX << "][" << nY << "] deep water wave - // height = " - // << m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight() << - // " deep water wave angle = " << - // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle() << - // endl; - n++; - } - } - - // // DEBUG CODE - // =========================================================================================================== - // string strOutFile = m_strOutPath; - // strOutFile += "init_deep_water_wave_height_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // GDALDriver* pDriver = - // GetGDALDriverManager()->GetDriverByName("gtiff"); GDALDataset* - // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, - // m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // double* pdRaster = new double[m_ulNumCells]; - // int nn = 0; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // // Write this value to the array - // pdRaster[nn] = - // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight(); nn++; - // } - // } - // - // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_nMissingValue); - // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, - // pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // // DEBUG CODE - // =========================================================================================================== - - // // DEBUG CODE - // =========================================================================================================== - // strOutFile = m_strOutPath; - // strOutFile += "init_deep_water_wave_angle_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, - // m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // nn = 0; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // // Write this value to the array - // pdRaster[nn] = - // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle(); nn++; - // } - // } - // - // pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_nMissingValue); - // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, - // pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // // DEBUG CODE - // =========================================================================================================== - - return RTN_OK; - } +/*! + \file gis_raster.cpp + \brief These functions use GDAL (at least version 2) to read and write raster + GIS files in several formats + \details TODO 001 A more detailed description of these routines. + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + + This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include + +#include + +#include +using std::atan2; +using std::hypot; +using std::isfinite; +using std::isnan; +using std::sqrt; + +#include +using std::vector; + +#include +using std::cerr; +using std::endl; +using std::ios; + +#include +using std::ifstream; + +#include +using std::stringstream; + +#include +using std::to_string; + +#include +#include +#include +#include +#include +#include + +#include "2di_point.h" +#include "cme.h" +#include "coast.h" +#include "simulation.h" +#include "spatial_interpolation.h" + +//=============================================================================================================================== +//! Initialize GDAL with performance optimizations +//=============================================================================================================================== +void CSimulation::InitializeGDALPerformance(void) { + // Configure GDAL for optimal performance + // Enable GDAL threading - use all available CPU cores +#ifdef _OPENMP + CPLSetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); +#else + CPLSetConfigOption("GDAL_NUM_THREADS", "4"); // Fallback for non-OpenMP builds +#endif + + // Optimize GDAL memory usage and caching + CPLSetConfigOption("GDAL_CACHEMAX", + "1.5GB"); // 2GB cache for large grids (was 1GB) + CPLSetConfigOption("GDAL_DISABLE_READDIR_ON_OPEN", + "EMPTY_DIR"); // Faster file access + CPLSetConfigOption("VSI_CACHE", "TRUE"); // Enable virtual file system cache + CPLSetConfigOption("VSI_CACHE_SIZE", "512MB"); // 256MB VSI cache + + // Block and chunk optimizations for raster operations + CPLSetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"); + CPLSetConfigOption("GDAL_RASTERIO_RESAMPLING", + "CUBIC"); // Better for coastal DEM data + + // Grid creation optimizations (for GDALGridCreate performance) + CPLSetConfigOption("GDAL_GRID_MAX_POINTS_PER_QUADTREE_LEAF", "1024"); + // Increased from 512 + CPLSetConfigOption("GDAL_GRID_POINT_COUNT_THRESHOLD", + "100"); // New 2024 option + + // Thread-safe dataset access (GDAL 3.10+) + CPLSetConfigOption("GDAL_DATASET_CACHE_SIZE", "64"); // Cache more datasets + + // Compression optimizations for output + CPLSetConfigOption("GDAL_TIFF_OVR_BLOCKSIZE", + "512"); // Optimal for coastal data + + // Memory allocator optimization for multi-threading + CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", "YES"); + + // Disable GDAL warnings for cleaner output (optional) + // CPLSetConfigOption("CPL_LOG", "/dev/null"); + // Debugging (remove in production) + // CPLSetConfigOption("CPL_DEBUG", "ON"); + // CPLSetConfigOption("GDAL_DEBUG", "ON"); + + m_bGDALOptimisations = true; +} + + //=============================================================================================================================== +//! Reads a raster DEM of basement elevation data to the Cell array +//=============================================================================================================================== +int CSimulation::nReadRasterBasementDEM(void) +{ + // Initialize GDAL performance settings (only needs to be done once) + static bool bGDALInitialized = false; + + if (! bGDALInitialized) + { + InitializeGDALPerformance(); + bGDALInitialized = true; + } + + // Use GDAL to create a dataset object, which then opens the DEM file + GDALDataset *pGDALDataset = static_cast(GDALOpen(m_strInitialBasementDEMFile.c_str(), GA_ReadOnly)); + + if (NULL == pGDALDataset) + { + // Can't open file (note will already have sent GDAL error message to stdout) + cerr << ERR << "cannot open " << m_strInitialBasementDEMFile << " for input: " << CPLGetLastErrorMsg() << endl; + return RTN_ERR_DEMFILE; + } + + // Opened OK, so get GDAL basement DEM dataset information + m_strGDALBasementDEMDriverCode = pGDALDataset->GetDriver()->GetDescription(); + m_strGDALBasementDEMDriverDesc = pGDALDataset->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); + m_strGDALBasementDEMProjection = pGDALDataset->GetProjectionRef(); + + if (m_strGDALBasementDEMProjection.empty()) + { + // TODO + m_strGDALBasementDEMProjection = ""; + // pGDALDataset->SetProjectionRef(m_strGDALBasementDEMProjection); + +// ENGCRS["Plane", +// EDATUM["Unknown engineering datum"], +// CS[Cartesian,2], +// AXIS["(E)",east, +// ORDER[1], +// LENGTHUNIT["Meter",1]], +// AXIS["(N)",north, +// ORDER[2], +// LENGTHUNIT["Meter",1]]] + } + else + { + // We have reference units, so check that they are in metres (note US spelling) + if (! m_strGDALBasementDEMProjection.empty()) + { + string const strTmp = strToLower(&m_strGDALBasementDEMProjection); + + if ((strTmp.find("meter") == string::npos) && (strTmp.find("metre") == string::npos)) + { + // error: x-y values must be in metres + cerr << ERR << "GIS file x-y values (" << m_strGDALBasementDEMProjection << ") in " << m_strInitialBasementDEMFile << " must be in metres" << endl; + return RTN_ERR_DEMFILE; + } + } + } + + // Now get dataset size, and do some rudimentary checks + m_nXGridSize = pGDALDataset->GetRasterXSize(); + + if (m_nXGridSize == 0) + { + // Error: silly number of columns specified + cerr << ERR << "invalid number of columns (" << m_nXGridSize << ") in " << m_strInitialBasementDEMFile << endl; + return RTN_ERR_DEMFILE; + } + + m_nYGridSize = pGDALDataset->GetRasterYSize(); + + if (m_nYGridSize == 0) + { + // Error: silly number of rows specified + cerr << ERR << "invalid number of rows (" << m_nYGridSize << ") in " << m_strInitialBasementDEMFile << endl; + return RTN_ERR_DEMFILE; + } + + // Get geotransformation info (see http://www.gdal.org/classGDALDataset.html) + if (CE_Failure == pGDALDataset->GetGeoTransform(m_dGeoTransform)) + { + // Can't get geotransformation (note will already have sent GDAL error message to stdout) + cerr << ERR << CPLGetLastErrorMsg() << " in " << m_strInitialBasementDEMFile << endl; + return RTN_ERR_DEMFILE; + } + + // CoastalME can only handle rasters that are oriented N-S and W-E. (If you need to work with a raster that is oriented differently, then you must rotate it before running CoastalME). So here we check whether row rotation (m_dGeoTransform[2]) and column rotation (m_dGeoTransform[4]) are both zero. See https://gdal.org/tutorials/geotransforms_tut.html + if ((! bFPIsEqual(m_dGeoTransform[2], 0.0, TOLERANCE)) || (! bFPIsEqual(m_dGeoTransform[4], 0.0, TOLERANCE))) + { + // Error: not oriented NS and W-E + cerr << ERR << m_strInitialBasementDEMFile << " is not oriented N-S and W-E. Row rotation = " << m_dGeoTransform[2] << " and column rotation = " << m_dGeoTransform[4] << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // Get the X and Y cell sizes, in external CRS units. Note that while the cell is supposed to be square, it may not be exactly so due to oddities with some GIS calculations + double const dCellSideX = tAbs(m_dGeoTransform[1]); + double const dCellSideY = tAbs(m_dGeoTransform[5]); + + // Check that the cell is more or less square + if (! bFPIsEqual(dCellSideX, dCellSideY, 1e-2)) + { + // Error: cell is not square enough + cerr << ERR << "cell is not square in " << m_strInitialBasementDEMFile << ", is " << dCellSideX << " x " << dCellSideY << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // Calculate the average length of cell side, the cell's diagonal, and the area of a cell (in external CRS units) + m_dCellSide = (dCellSideX + dCellSideY) / 2.0; + m_dCellArea = m_dCellSide * m_dCellSide; + m_dCellDiagonal = hypot(m_dCellSide, m_dCellSide); + + // And calculate the inverse values + m_dInvCellSide = 1 / m_dCellSide; + m_dInvCellDiagonal = 1 / m_dCellDiagonal; + + // Save some values in external CRS + m_dNorthWestXExtCRS = m_dGeoTransform[0] - (m_dGeoTransform[1] / 2); + m_dNorthWestYExtCRS = m_dGeoTransform[3] - (m_dGeoTransform[5] / 2); + m_dSouthEastXExtCRS = m_dGeoTransform[0] + (m_nXGridSize * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2); + m_dSouthEastYExtCRS = m_dGeoTransform[3] + (m_nYGridSize * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2); + + // And calc the grid area in external CRS units + m_dExtCRSGridArea = tAbs(m_dNorthWestXExtCRS - m_dSouthEastXExtCRS) * tAbs(m_dNorthWestYExtCRS * m_dSouthEastYExtCRS); + + // Now get GDAL raster band information + GDALRasterBand *pGDALBand = pGDALDataset->GetRasterBand(1); + int nBlockXSize = 0, nBlockYSize = 0; + pGDALBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + m_strGDALBasementDEMDataType = GDALGetDataTypeName(pGDALBand->GetRasterDataType()); + + // If we have value units, then check them + string const strUnits = pGDALBand->GetUnitType(); + + if ((!strUnits.empty()) && (strUnits.find('m') == string::npos)) + { + // Error: value units must be m + cerr << ERR << "DEM vertical units are (" << strUnits << " ) in " << m_strInitialBasementDEMFile << ", should be 'm'" << endl; + return RTN_ERR_DEMFILE; + } + + // If present, get the missing value (NODATA) setting + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails + double const dMissingValue = pGDALBand->GetNoDataValue(); // Will fail for some formats + CPLPopErrorHandler(); + + if (! bFPIsEqual(dMissingValue, m_dMissingValue, TOLERANCE)) + { + cerr << " " << NOTE << "NODATA value in " << m_strInitialBasementDEMFile << " is " << dMissingValue << "\n instead using CoastalME's default floating-point NODATA value " << m_dMissingValue << endl; + } + + // Next allocate memory for a 2D array of raster cell objects: tell the user what is happening + AnnounceAllocateMemory(); + int const nRet = m_pRasterGrid->nCreateGrid(); + + if (nRet != RTN_OK) + return nRet; + + // Allocate memory for a 1D floating-point array, to hold the scan line for GDAL + double *pdScanline = new double[m_nXGridSize]; + + if (NULL == pdScanline) + { + // Error, can't allocate memory + cerr << ERR << "cannot allocate memory for " << m_nXGridSize << " x 1D array" << endl; + return (RTN_ERR_MEMALLOC); + } + + // Now read in the data + for (int j = 0; j < m_nYGridSize; j++) + { + // Read scanline + if (CE_Failure == pGDALBand->RasterIO(GF_Read, 0, j, m_nXGridSize, 1, pdScanline, m_nXGridSize, 1, GDT_Float64, 0, 0, NULL)) + { + // Error while reading scanline + cerr << ERR << CPLGetLastErrorMsg() << " in " << m_strInitialBasementDEMFile << endl; + return RTN_ERR_DEMFILE; + } + + // All OK, so read scanline into cell elevations (including any missing values) + for (int i = 0; i < m_nXGridSize; i++) + { + double dTmp = pdScanline[i]; + + if ((isnan(dTmp)) || (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) + dTmp = m_dMissingValue; + + m_pRasterGrid->m_Cell[i][j].SetBasementElev(dTmp); + } + } + + // Finished, so get rid of dataset object + GDALClose(pGDALDataset); + + // Get rid of memory allocated to this array + delete[] pdScanline; + + return RTN_OK; +} + +//=============================================================================================================================== +//! Mark cells which are at the edge of a bounding box which represents the +//! valid part of the grid, as defined by the basement layer. The valid part of +//! the grid may be the whole grid, or only part of the whole grid. The bounding +//! box may be an irregular shape (but may not have re-entrant edges): simple +//! shapes are more likely to work correctly +//=============================================================================================================================== +int CSimulation::nMarkBoundingBoxEdgeCells(void) { + // The bounding box must touch the edge of the grid at least once on each side + // of the grid, so store these points. Search in a clockwise direction around + // the edge of the grid + vector VPtiBoundingBoxCorner; + + // Start with the top (north) edge + bool bFound = false; + + for (int nX = 0; nX < m_nXGridSize; nX++) { + if (bFound) + break; + + for (int nY = 0; nY < m_nYGridSize; nY++) { + if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + CGeom2DIPoint const PtiTmp(nX, nY); + VPtiBoundingBoxCorner.push_back(PtiTmp); + bFound = true; + break; + } + } + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": north (top) edge of bounding box not found" + << endl; + + return RTN_ERR_BOUNDING_BOX; + } + + // Do the same for the right (east) edge + bFound = false; + + for (int nY = 0; nY < m_nYGridSize; nY++) { + if (bFound) + break; + + for (int nX = m_nXGridSize - 1; nX >= 0; nX--) { + if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + CGeom2DIPoint const PtiTmp(nX, nY); + VPtiBoundingBoxCorner.push_back(PtiTmp); + bFound = true; + break; + } + } + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": east (right) edge of bounding box not found" + << endl; + + return RTN_ERR_BOUNDING_BOX; + } + + // Do the same for the south (bottom) edge + bFound = false; + + for (int nX = m_nXGridSize - 1; nX >= 0; nX--) { + if (bFound) + break; + + for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { + if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + CGeom2DIPoint const PtiTmp(nX, nY); + VPtiBoundingBoxCorner.push_back(PtiTmp); + bFound = true; + break; + } + } + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": south (bottom) edge of bounding box not found" + << endl; + + return RTN_ERR_BOUNDING_BOX; + } + + // And finally repeat for the west (left) edge + bFound = false; + + for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { + if (bFound) + break; + + for (int nX = 0; nX < m_nXGridSize; nX++) { + if (!m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + CGeom2DIPoint const PtiTmp(nX, nY); + VPtiBoundingBoxCorner.push_back(PtiTmp); + bFound = true; + break; + } + } + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_ALL) + LogStream << m_ulIter << ": west (left) edge of bounding box not found" + << endl; + + return RTN_ERR_BOUNDING_BOX; + } + + // OK, so we have a point on each side of the grid, so start at this point and + // find the edges of the bounding box. Go round in a clockwise direction: top + // (north) edge first + for (int nX = VPtiBoundingBoxCorner[0].nGetX(); + nX <= VPtiBoundingBoxCorner[1].nGetX(); nX++) { + bFound = false; + + for (int nY = VPtiBoundingBoxCorner[0].nGetY(); nY < m_nYGridSize; nY++) { + if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + m_ulMissingValueBasementCells++; + continue; + } + + // Found a bounding box edge cell + m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(NORTH); + + m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); + m_VEdgeCellEdge.push_back(NORTH); + + bFound = true; + break; + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream + << m_ulIter + << ": could not find a bounding box edge cell for grid column " + << nX << endl; + + return RTN_ERR_BOUNDING_BOX; + } + } + + // Right (east) edge + for (int nY = VPtiBoundingBoxCorner[1].nGetY(); + nY <= VPtiBoundingBoxCorner[2].nGetY(); nY++) { + bFound = false; + + for (int nX = VPtiBoundingBoxCorner[1].nGetX(); nX >= 0; nX--) { + if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + m_ulMissingValueBasementCells++; + continue; + } + + // Found a bounding box edge cell + m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(EAST); + + m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); + m_VEdgeCellEdge.push_back(EAST); + + bFound = true; + break; + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter + << ": could not find a bounding box edge cell for grid row " + << nY << endl; + + return RTN_ERR_BOUNDING_BOX; + } + } + + // Bottom (south) edge + for (int nX = VPtiBoundingBoxCorner[2].nGetX(); + nX >= VPtiBoundingBoxCorner[3].nGetX(); nX--) { + bFound = false; + + for (int nY = VPtiBoundingBoxCorner[2].nGetY(); nY >= 0; nY--) { + if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + m_ulMissingValueBasementCells++; + continue; + } + + // Found a bounding box edge cell + m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(SOUTH); + + m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); + m_VEdgeCellEdge.push_back(SOUTH); + + bFound = true; + break; + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream + << m_ulIter + << ": could not find a bounding box edge cell for grid column " + << nX << endl; + + return RTN_ERR_BOUNDING_BOX; + } + } + + // Left (west) edge + for (int nY = VPtiBoundingBoxCorner[3].nGetY(); + nY >= VPtiBoundingBoxCorner[0].nGetY(); nY--) { + for (int nX = VPtiBoundingBoxCorner[3].nGetX(); nX < m_nXGridSize - 1; nX++) + // for (int nX = VPtiBoundingBoxCorner[3].nGetX(); nX < + // VPtiBoundingBoxCorner[3].nGetX(); nX++) + { + if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) { + m_ulMissingValueBasementCells++; + continue; + } + + // Found a bounding box edge cell + m_pRasterGrid->m_Cell[nX][nY].SetBoundingBoxEdge(WEST); + + m_VEdgeCell.push_back(CGeom2DIPoint(nX, nY)); + m_VEdgeCellEdge.push_back(WEST); + + bFound = true; + break; + } + + if (!bFound) { + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << m_ulIter + << ": could not find a bounding box edge cell for grid row " + << nY << endl; + + return RTN_ERR_BOUNDING_BOX; + } + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! Reads raster GIS datafiles into the RasterGrid array +//=============================================================================================================================== +int CSimulation::nReadRasterGISFile(int const nDataItem, int const nLayer) { + string strGISFile; + string strDriverCode; + string strDriverDesc; + string strProjection; + string strDataType; + + switch (nDataItem) { + case (LANDFORM_RASTER): + // Initial Landform Class GIS data + strGISFile = m_strInitialLandformFile; + break; + + case (INTERVENTION_CLASS_RASTER): + // Intervention class + strGISFile = m_strInterventionClassFile; + break; + + case (INTERVENTION_HEIGHT_RASTER): + // Intervention height + strGISFile = m_strInterventionHeightFile; + break; + + case (SUSP_SED_RASTER): + // Initial Suspended Sediment GIS data + strGISFile = m_strInitialSuspSedimentFile; + break; + + case (FINE_UNCONS_RASTER): + // Initial Unconsolidated Fine Sediment GIS data + strGISFile = m_VstrInitialFineUnconsSedimentFile[nLayer]; + break; + + case (SAND_UNCONS_RASTER): + // Initial Unconsolidated Sand Sediment GIS data + strGISFile = m_VstrInitialSandUnconsSedimentFile[nLayer]; + break; + + case (COARSE_UNCONS_RASTER): + // Initial Unconsolidated Coarse Sediment GIS data + strGISFile = m_VstrInitialCoarseUnconsSedimentFile[nLayer]; + break; + + case (FINE_CONS_RASTER): + // Initial Consolidated Fine Sediment GIS data + strGISFile = m_VstrInitialFineConsSedimentFile[nLayer]; + break; + + case (SAND_CONS_RASTER): + // Initial Consolidated Sand Sediment GIS data + strGISFile = m_VstrInitialSandConsSedimentFile[nLayer]; + break; + + case (COARSE_CONS_RASTER): + // Initial Consolidated Coarse Sediment GIS data + strGISFile = m_VstrInitialCoarseConsSedimentFile[nLayer]; + break; + } + + // Use GDAL to create a dataset object, which then opens the GIS file + GDALDataset *pGDALDataset = + static_cast(GDALOpen(strGISFile.c_str(), GA_ReadOnly)); + + if (NULL == pGDALDataset) { + // Can't open file (note will already have sent GDAL error message to + // stdout) + cerr << ERR << "cannot open " << strGISFile + << " for input: " << CPLGetLastErrorMsg() << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // Opened OK, so get dataset information + strDriverCode = pGDALDataset->GetDriver()->GetDescription(); + strDriverDesc = pGDALDataset->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); + strProjection = pGDALDataset->GetProjectionRef(); + + // Get geotransformation info + double dGeoTransform[6]; + if (CE_Failure == pGDALDataset->GetGeoTransform(dGeoTransform)) { + // Can't get geotransformation (note will already have sent GDAL error + // message to stdout) + cerr << ERR << CPLGetLastErrorMsg() << " in " << strGISFile << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // Now get dataset size, and do some checks + int const nTmpXSize = pGDALDataset->GetRasterXSize(); + if (nTmpXSize != m_nXGridSize) { + // Error: incorrect number of columns specified + cerr << ERR << "different number of columns in " << strGISFile << " (" + << nTmpXSize << ") and " << m_strInitialBasementDEMFile << "(" + << m_nXGridSize << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + int const nTmpYSize = pGDALDataset->GetRasterYSize(); + if (nTmpYSize != m_nYGridSize) { + // Error: incorrect number of rows specified + cerr << ERR << "different number of rows in " << strGISFile << " (" + << nTmpYSize << ") and " << m_strInitialBasementDEMFile << " (" + << m_nYGridSize << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + double dTmp = m_dGeoTransform[0] - (m_dGeoTransform[1] / 2); + if (!bFPIsEqual(dTmp, m_dNorthWestXExtCRS, TOLERANCE)) { + // Error: different min x from DEM file + cerr << ERR << "different min x values in " << strGISFile << " (" << dTmp + << ") and " << m_strInitialBasementDEMFile << " (" + << m_dNorthWestXExtCRS << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + dTmp = m_dGeoTransform[3] - (m_dGeoTransform[5] / 2); + if (!bFPIsEqual(dTmp, m_dNorthWestYExtCRS, TOLERANCE)) { + // Error: different min x from DEM file + cerr << ERR << "different min y values in " << strGISFile << " (" << dTmp + << ") and " << m_strInitialBasementDEMFile << " (" + << m_dNorthWestYExtCRS << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + double const dTmpResX = tAbs(dGeoTransform[1]); + if (!bFPIsEqual(dTmpResX, m_dCellSide, 1e-2)) { + // Error: different cell size in X direction: note that due to rounding + // errors in some GIS packages, must expect some discrepancies + cerr << ERR << "cell size in X direction (" << dTmpResX << ") in " + << strGISFile << " differs from cell size in of basement DEM (" + << m_dCellSide << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + double const dTmpResY = tAbs(dGeoTransform[5]); + if (!bFPIsEqual(dTmpResY, m_dCellSide, 1e-2)) { + // Error: different cell size in Y direction: note that due to rounding + // errors in some GIS packages, must expect some discrepancies + cerr << ERR << "cell size in Y direction (" << dTmpResY << ") in " + << strGISFile << " differs from cell size of basement DEM (" + << m_dCellSide << ")" << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // Now get GDAL raster band information + GDALRasterBand *pGDALBand = pGDALDataset->GetRasterBand( + 1); // TODO 028 Give a message if there are several bands + int nBlockXSize = 0, nBlockYSize = 0; + pGDALBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + strDataType = GDALGetDataTypeName(pGDALBand->GetRasterDataType()); + + switch (nDataItem) { + case (LANDFORM_RASTER): + // Initial Landform Class GIS data + m_strGDALLDriverCode = strDriverCode; + m_strGDALLDriverDesc = strDriverDesc; + m_strGDALLProjection = strProjection; + m_strGDALLDataType = strDataType; + break; + + case (INTERVENTION_CLASS_RASTER): + // Intervention class + m_strGDALICDriverCode = strDriverCode; + m_strGDALICDriverDesc = strDriverDesc; + m_strGDALICProjection = strProjection; + m_strGDALICDataType = strDataType; + break; + + case (INTERVENTION_HEIGHT_RASTER): + // Intervention height + m_strGDALIHDriverCode = strDriverCode; + m_strGDALIHDriverDesc = strDriverDesc; + m_strGDALIHProjection = strProjection; + m_strGDALIHDataType = strDataType; + break; + + case (SUSP_SED_RASTER): + // Initial Suspended Sediment GIS data + m_strGDALISSDriverCode = strDriverCode; + m_strGDALISSDriverDesc = strDriverDesc; + m_strGDALISSProjection = strProjection; + m_strGDALISSDataType = strDataType; + break; + + case (FINE_UNCONS_RASTER): + // Initial Unconsolidated Fine Sediment GIS data + m_VstrGDALIUFDriverCode[nLayer] = strDriverCode; + m_VstrGDALIUFDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALIUFProjection[nLayer] = strProjection; + m_VstrGDALIUFDataType[nLayer] = strDataType; + break; + + case (SAND_UNCONS_RASTER): + // Initial Unconsolidated Sand Sediment GIS data + m_VstrGDALIUSDriverCode[nLayer] = strDriverCode; + m_VstrGDALIUSDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALIUSProjection[nLayer] = strProjection; + m_VstrGDALIUSDataType[nLayer] = strDataType; + break; + + case (COARSE_UNCONS_RASTER): + // Initial Unconsolidated Coarse Sediment GIS data + m_VstrGDALIUCDriverCode[nLayer] = strDriverCode; + m_VstrGDALIUCDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALIUCProjection[nLayer] = strProjection; + m_VstrGDALIUCDataType[nLayer] = strDataType; + break; + + case (FINE_CONS_RASTER): + // Initial Consolidated Fine Sediment GIS data + m_VstrGDALICFDriverCode[nLayer] = strDriverCode; + m_VstrGDALICFDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALICFProjection[nLayer] = strProjection; + m_VstrGDALICFDataType[nLayer] = strDataType; + break; + + case (SAND_CONS_RASTER): + // Initial Consolidated Sand Sediment GIS data + m_VstrGDALICSDriverCode[nLayer] = strDriverCode; + m_VstrGDALICSDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALICSProjection[nLayer] = strProjection; + m_VstrGDALICSDataType[nLayer] = strDataType; + break; + + case (COARSE_CONS_RASTER): + // Initial Consolidated Coarse Sediment GIS data + m_VstrGDALICCDriverCode[nLayer] = strDriverCode; + m_VstrGDALICCDriverDesc[nLayer] = strDriverDesc; + m_VstrGDALICCProjection[nLayer] = strProjection; + m_VstrGDALICCDataType[nLayer] = strDataType; + break; + } + + // If present, get the missing value setting + string const strTmp = strToLower(&strDataType); + if (strTmp.find("int") != string::npos) { + // This is an integer layer + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to + // fail silently, if it fails + m_nGISMissingValue = static_cast( + pGDALBand->GetNoDataValue()); // Note will fail for some formats + CPLPopErrorHandler(); + + if (m_nGISMissingValue != m_nMissingValue) { + cerr + << " " << NOTE << "NODATA value in " << strGISFile << " is " + << m_nGISMissingValue + << "\n instead using CoatalME's default integer NODATA value " + << m_nMissingValue << endl; + } + } else { + // This is a floating point layer + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to + // fail silently, if it fails + m_dGISMissingValue = + pGDALBand->GetNoDataValue(); // Note will fail for some formats + CPLPopErrorHandler(); + + if (!bFPIsEqual(m_dGISMissingValue, m_dMissingValue, TOLERANCE)) { + cerr << " " << NOTE << "NODATA value in " << strGISFile << " is " + << m_dGISMissingValue + << "\n instead using CoastalME's default floating-point " + "NODATA value " + << m_dMissingValue << endl; + } + } + + // Allocate memory for a 1D array, to hold the scan line for GDAL + double *pdScanline = new double[m_nXGridSize]; + if (NULL == pdScanline) { + // Error, can't allocate memory + cerr << ERR << "cannot allocate memory for " << m_nXGridSize + << " x 1D array" << endl; + return (RTN_ERR_MEMALLOC); + } + + // Now read in the data + int nMissing = 0; + + for (int nY = 0; nY < m_nYGridSize; nY++) { + // Read scanline + if (CE_Failure == pGDALBand->RasterIO(GF_Read, 0, nY, m_nXGridSize, 1, + pdScanline, m_nXGridSize, 1, + GDT_Float64, 0, 0, NULL)) { + // Error while reading scanline + cerr << ERR << CPLGetLastErrorMsg() << " in " << strGISFile << endl; + return (RTN_ERR_RASTER_FILE_READ); + } + + // All OK, so read scanline into cells (including any missing values) + for (int nX = 0; nX < m_nXGridSize; nX++) { + int nTmp; + + switch (nDataItem) { + case (LANDFORM_RASTER): + // Initial Landform Class GIS data, is integer TODO 030 Do we also need + // a landform sub-category input? + nTmp = static_cast(pdScanline[nX]); + + if ((isnan(nTmp)) || (nTmp == m_nGISMissingValue)) { + nTmp = m_nMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->SetLFCategory(nTmp); + break; + + case (INTERVENTION_CLASS_RASTER): + // Intervention class, is integer. If not an intervention, show INT_NODATA + nTmp = static_cast(pdScanline[nX]); + + if ((isnan(nTmp)) || (nTmp == m_nGISMissingValue)) + { + nTmp = m_nMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->SetLFCategory(nTmp); + break; + + case (INTERVENTION_HEIGHT_RASTER): + // Intervention height + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY].SetInterventionHeight(dTmp); + break; + + case (SUSP_SED_RASTER): + // Initial Suspended Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY].SetSuspendedSediment(dTmp); + break; + + case (FINE_UNCONS_RASTER): + // Initial Unconsolidated Fine Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetUnconsolidatedSediment() + ->SetFineDepth(dTmp); + break; + + case (SAND_UNCONS_RASTER): + // Initial Unconsolidated Sand Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetUnconsolidatedSediment() + ->SetSandDepth(dTmp); + break; + + case (COARSE_UNCONS_RASTER): + // Initial Unconsolidated Coarse Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetUnconsolidatedSediment() + ->SetCoarseDepth(dTmp); + break; + + case (FINE_CONS_RASTER): + // Initial Consolidated Fine Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetConsolidatedSediment() + ->SetFineDepth(dTmp); + break; + + case (SAND_CONS_RASTER): + // Initial Consolidated Sand Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetConsolidatedSediment() + ->SetSandDepth(dTmp); + break; + + case (COARSE_CONS_RASTER): + // Initial Consolidated Coarse Sediment GIS data + dTmp = pdScanline[nX]; + + if ((isnan(dTmp)) || + (bFPIsEqual(dTmp, m_dGISMissingValue, TOLERANCE))) { + dTmp = m_dMissingValue; + nMissing++; + } + + m_pRasterGrid->m_Cell[nX][nY] + .pGetLayerAboveBasement(nLayer) + ->pGetConsolidatedSediment() + ->SetCoarseDepth(dTmp); + break; + } + } + } + + // Finished, so get rid of dataset object + GDALClose(pGDALDataset); + + // Get rid of memory allocated to this array + delete[] pdScanline; + + if (nMissing > 0) { + cerr << WARN << nMissing << " missing values in " << strGISFile << endl; + LogStream << WARN << nMissing << " missing values in " << strGISFile + << endl; + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! Writes GIS raster files using GDAL, using data from the RasterGrid array +//=============================================================================================================================== +bool CSimulation::bWriteRasterGISFile(int const nDataItem, + string const *strPlotTitle, + int const nLayer, double const dElev) { + bool bIsInteger = false; + bool bIsUnsignedLong = false; + + // Begin constructing the file name for this save + string strFilePathName(m_strOutPath); + string strLayer = "_layer_"; + + stringstream ststrTmp; + + strLayer.append(to_string(nLayer + 1)); + + switch (nDataItem) { + case (RASTER_PLOT_BASEMENT_ELEVATION): + strFilePathName.append(RASTER_BASEMENT_ELEVATION_NAME); + break; + + case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): + strFilePathName.append(RASTER_SEDIMENT_TOP_ELEVATION_NAME); + break; + + case (RASTER_PLOT_TOP_ELEV_INC_SEA): + strFilePathName.append(RASTER_TOP_ELEVATION_INC_SEA_NAME); + break; + + case (RASTER_PLOT_TALUS): + strFilePathName.append(RASTER_TALUS_NAME); + break; + + case (RASTER_PLOT_CONS_SED_SLOPE): + strFilePathName.append(RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_NAME); + break; + + case (RASTER_PLOT_SLOPE_FOR_CLIFF_TOE): + strFilePathName.append(RASTER_SLOPE_FOR_CLIFF_TOE_NAME); + break; + + case (RASTER_PLOT_CLIFF_TOE): + strFilePathName.append(RASTER_CLIFF_TOE_NAME); + break; + + case (RASTER_PLOT_SEA_DEPTH): + strFilePathName.append(RASTER_SEA_DEPTH_NAME); + break; + + case (RASTER_PLOT_AVG_SEA_DEPTH): + strFilePathName.append(RASTER_AVG_SEA_DEPTH_NAME); + break; + + case (RASTER_PLOT_WAVE_HEIGHT): + strFilePathName.append(RASTER_WAVE_HEIGHT_NAME); + break; + + case (RASTER_PLOT_AVG_WAVE_HEIGHT): + strFilePathName.append(RASTER_AVG_WAVE_HEIGHT_NAME); + break; + + case (RASTER_PLOT_WAVE_ORIENTATION): + strFilePathName.append(RASTER_WAVE_ORIENTATION_NAME); + break; + + case (RASTER_PLOT_AVG_WAVE_ORIENTATION): + strFilePathName.append(RASTER_AVG_WAVE_ORIENTATION_NAME); + break; + + case (RASTER_PLOT_BEACH_PROTECTION): + strFilePathName.append(RASTER_BEACH_PROTECTION_NAME); + break; + + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): + strFilePathName.append(RASTER_POTENTIAL_PLATFORM_EROSION_NAME); + break; + + case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): + strFilePathName.append(RASTER_ACTUAL_PLATFORM_EROSION_NAME); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): + strFilePathName.append(RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_NAME); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): + strFilePathName.append(RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_NAME); + break; + + case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): + strFilePathName.append(RASTER_POTENTIAL_BEACH_EROSION_NAME); + break; + + case (RASTER_PLOT_ACTUAL_BEACH_EROSION): + strFilePathName.append(RASTER_ACTUAL_BEACH_EROSION_NAME); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): + strFilePathName.append(RASTER_TOTAL_POTENTIAL_BEACH_EROSION_NAME); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): + strFilePathName.append(RASTER_TOTAL_ACTUAL_BEACH_EROSION_NAME); + break; + + case (RASTER_PLOT_BEACH_DEPOSITION): + strFilePathName.append(RASTER_BEACH_DEPOSITION_NAME); + break; + + case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): + strFilePathName.append(RASTER_TOTAL_BEACH_DEPOSITION_NAME); + break; + + case (RASTER_PLOT_SUSPENDED_SEDIMENT): + strFilePathName.append(RASTER_SUSP_SED_NAME); + break; + + case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): + strFilePathName.append(RASTER_AVG_SUSP_SED_NAME); + break; + + case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_FINE_UNCONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_SAND_UNCONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_COARSE_UNCONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_FINE_CONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_SAND_CONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): + strFilePathName.append(RASTER_COARSE_CONS_NAME); + strFilePathName.append(strLayer); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_FINE_NAME); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_SAND_NAME); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_EROSION_COARSE_NAME); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): + strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_NAME); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): + strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_NAME); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): + strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_NAME); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): + strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_NAME); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): + strFilePathName.append(RASTER_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_NAME); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): + strFilePathName.append(RASTER_CLIFF_COLLAPSE_TIMESTEP_NAME); + break; + + case (RASTER_PLOT_CLIFF_NOTCH_ALL): + strFilePathName.append(RASTER_CLIFF_NOTCH_ALL_NAME); + break; + + case (RASTER_PLOT_INTERVENTION_HEIGHT): + strFilePathName.append(RASTER_INTERVENTION_HEIGHT_NAME); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): + strFilePathName.append(RASTER_DEEP_WATER_WAVE_ORIENTATION_NAME); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): + strFilePathName.append(RASTER_DEEP_WATER_WAVE_HEIGHT_NAME); + break; + + case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): + strFilePathName.append(RASTER_POLYGON_GAIN_OR_LOSS_NAME); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): + strFilePathName.append(RASTER_WAVE_PERIOD_NAME); + break; + + case (RASTER_PLOT_SEDIMENT_INPUT): + strFilePathName.append(RASTER_SEDIMENT_INPUT_EVENT_NAME); + break; + + case (RASTER_PLOT_BEACH_MASK): + bIsInteger = true; + strFilePathName.append(RASTER_BEACH_MASK_NAME); + break; + + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): + bIsInteger = true; + strFilePathName.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_NAME); + break; + + case (RASTER_PLOT_INUNDATION_MASK): + bIsInteger = true; + strFilePathName.append(RASTER_INUNDATION_MASK_NAME); + break; + + case (RASTER_PLOT_SLICE): + bIsInteger = true; + ststrTmp.str(""); + ststrTmp.clear(); + + // TODO 031 Get working for multiple slices + strFilePathName.append(RASTER_SLICE_NAME); + ststrTmp << "_" << dElev << "_"; + strFilePathName.append(ststrTmp.str()); + break; + + case (RASTER_PLOT_LANDFORM): + bIsInteger = true; + strFilePathName.append(RASTER_LANDFORM_NAME); + break; + + case (RASTER_PLOT_INTERVENTION_CLASS): + bIsInteger = true; + strFilePathName.append(RASTER_INTERVENTION_CLASS_NAME); + break; + + case (RASTER_PLOT_COAST): + bIsInteger = true; + strFilePathName.append(RASTER_COAST_NAME); + break; + + case (RASTER_PLOT_NORMAL_PROFILE): + bIsInteger = true; + strFilePathName.append(RASTER_COAST_NORMAL_NAME); + break; + + case (RASTER_PLOT_ACTIVE_ZONE): + bIsInteger = true; + strFilePathName.append(RASTER_ACTIVE_ZONE_NAME); + break; + + case (RASTER_PLOT_POLYGON): + bIsInteger = true; + strFilePathName.append(RASTER_POLYGON_NAME); + break; + + case (RASTER_PLOT_SHADOW_ZONE): + bIsInteger = true; + strFilePathName.append(RASTER_SHADOW_ZONE_NAME); + break; + + case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): + bIsInteger = true; + strFilePathName.append(RASTER_SHADOW_DOWNDRIFT_ZONE_NAME); + break; + + case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): + bIsInteger = true; + strFilePathName.append(RASTER_POLYGON_UPDRIFT_OR_DOWNDRIFT_NAME); + break; + + case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): + bIsInteger = true; + strFilePathName.append(RASTER_SETUP_SURGE_FLOOD_MASK_NAME); + break; + + case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): + bIsInteger = true; + strFilePathName.append(RASTER_SETUP_SURGE_RUNUP_FLOOD_MASK_NAME); + break; + + case (RASTER_PLOT_WAVE_FLOOD_LINE): + bIsInteger = true; + strFilePathName.append(RASTER_WAVE_FLOOD_LINE_NAME); + break; + } + + // Append the 'save number' to the filename, and prepend zeros to the save + // number + ststrTmp.str(""); + ststrTmp.clear(); + + strFilePathName.append("_"); + + if (m_bGISSaveDigitsSequential) { + // Save number is m_bGISSaveDigitsSequential + ststrTmp << FillToWidth('0', m_nGISMaxSaveDigits) << m_nGISSave; + } else { + // Save number is iteration + ststrTmp << FillToWidth('0', m_nGISMaxSaveDigits) << m_ulIter; + } + + strFilePathName.append(ststrTmp.str()); + + // Finally, maybe append the extension + if (!m_strGDALRasterOutputDriverExtension.empty()) { + strFilePathName.append("."); + strFilePathName.append(m_strGDALRasterOutputDriverExtension); + } + + // TODO 065 Used to try to debug floating point exception in pDriver->Create() + // below CPLSetConfigOption("CPL_DEBUG", "ON"); + // CPLSetConfigOption("GDAL_NUM_THREADS", "1"); + + GDALDriver *pDriver; + GDALDataset *pDataSet; + + if (m_bGDALCanCreate) { + // The user-requested raster driver supports the Create() method + pDriver = GetGDALDriverManager()->GetDriverByName( + m_strRasterGISOutFormat.c_str()); + + if (bIsInteger) { + pDataSet = + pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, + 1, GDT_Int16, m_papszGDALRasterOptions); + } else if (bIsUnsignedLong) { + pDataSet = + pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, + 1, GDT_UInt32, m_papszGDALRasterOptions); + + } else if (m_strRasterGISOutFormat == "gpkg") { + // TODO 065 Floating point exception here + pDataSet = + pDriver->Create(strFilePathName.c_str(), m_nXGridSize, m_nYGridSize, + 1, GDT_Byte, m_papszGDALRasterOptions); + } else { + pDataSet = pDriver->Create(strFilePathName.c_str(), m_nXGridSize, + m_nYGridSize, 1, m_GDALWriteFloatDataType, + m_papszGDALRasterOptions); + } + + if (NULL == pDataSet) { + // Error, couldn't create file + cerr << ERR << "cannot create " << m_strRasterGISOutFormat + << " file named " << strFilePathName << endl; + return false; + } + } + else + { + // The user-requested raster driver does not support the Create() method, so we must first create a memory-file dataset + pDriver = GetGDALDriverManager()->GetDriverByName("MEM"); + pDataSet = pDriver->Create("", m_nXGridSize, m_nYGridSize, 1, m_GDALWriteFloatDataType, NULL); + + if (NULL == pDataSet) + { + // Couldn't create in-memory file dataset + cerr << ERR << "cannot create in-memory file for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl + << CPLGetLastErrorMsg() << endl; + return false; + } + } + + // Set projection info for output dataset (will be same as was read in from basement DEM) + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails + pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); // Will fail for some formats + CPLPopErrorHandler(); + + // Set geotransformation info for output dataset (will be same as was read in from DEM) + if (CE_Failure == pDataSet->SetGeoTransform(m_dGeoTransform)) + LogStream << WARN << "cannot write geotransformation information to " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl << CPLGetLastErrorMsg() << endl; + + // Allocate memory for a 1D array, to hold the floating point raster band data for GDAL + double *pdRaster = new double[m_ulNumCells]; + if (NULL == pdRaster) + { + // Error, can't allocate memory + cerr << ERR << "cannot allocate memory for " << m_ulNumCells << " x 1D floating-point array for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl; + return (RTN_ERR_MEMALLOC); + } + + bool bScaleOutput = false; + double dRangeScale = 0; + double dDataMin = 0; + + if (! m_bGDALCanWriteFloat) + { + double dDataMax = 0; + + // The output file format cannot handle floating-point numbers, so we may need to scale the output + GetRasterOutputMinMax(nDataItem, dDataMin, dDataMax, nLayer, 0); + + double const dDataRange = dDataMax - dDataMin; + double const dWriteRange = static_cast(m_lGDALMaxCanWrite - m_lGDALMinCanWrite); + + if (dDataRange > 0) + dRangeScale = dWriteRange / dDataRange; + + // If we are attempting to write values which are outside this format's allowable range, and the user has set the option, then scale the output + if (((dDataMin < static_cast(m_lGDALMinCanWrite)) || (dDataMax > static_cast(m_lGDALMaxCanWrite))) && m_bScaleRasterOutput) + bScaleOutput = true; + } + + // Fill the array + int n = 0; + int nPoly = 0; + int nPolyCoast = 0; + int nTopLayer = 0; + double dTmp = 0; + + for (int nY = 0; nY < m_nYGridSize; nY++) + { + for (int nX = 0; nX < m_nXGridSize; nX++) + { + switch (nDataItem) + { + case (RASTER_PLOT_BASEMENT_ELEVATION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBasementElev(); + break; + + case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus(); + break; + + case (RASTER_PLOT_TOP_ELEV_INC_SEA): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTopElevIncSea(); + break; + + case (RASTER_PLOT_TALUS): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTalusDepth(); + break; + + case (RASTER_PLOT_CONS_SED_SLOPE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetConsSedSlope(); + break; + + case (RASTER_PLOT_SLOPE_FOR_CLIFF_TOE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSlopeForCliffToe(); + break; + + case (RASTER_PLOT_CLIFF_TOE): + dTmp = static_cast(m_pRasterGrid->m_Cell[nX][nY].bIsCliffToe()); + break; + + case (RASTER_PLOT_SEA_DEPTH): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSeaDepth(); + break; + + case (RASTER_PLOT_AVG_SEA_DEPTH): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSeaDepth() / static_cast(m_ulIter); + break; + + case (RASTER_PLOT_WAVE_HEIGHT): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); + else + dTmp = 0; + break; + + case (RASTER_PLOT_AVG_WAVE_HEIGHT): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveHeight() / static_cast(m_ulIter); + else + dTmp = 0; + break; + + case (RASTER_PLOT_WAVE_ORIENTATION): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); + else + dTmp = 0; + break; + + case (RASTER_PLOT_AVG_WAVE_ORIENTATION): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveAngle() / static_cast(m_ulIter); + else + dTmp = 0; + break; + + case (RASTER_PLOT_BEACH_PROTECTION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachProtectionFactor(); + + if (bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) + dTmp = m_dMissingValue; + else + dTmp = 1 - dTmp; // Output the inverse, seems more intuitive + break; + + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialPlatformErosion(); + break; + + case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualPlatformErosion(); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialPlatformErosion(); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualPlatformErosion(); + break; + + case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialBeachErosion(); + break; + + case (RASTER_PLOT_ACTUAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualBeachErosion(); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialBeachErosion(); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualBeachErosion(); + break; + + case (RASTER_PLOT_BEACH_DEPOSITION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachDeposition(); + break; + + case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotBeachDeposition(); + break; + + case (RASTER_PLOT_SUSPENDED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSuspendedSediment(); + break; + + case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSuspendedSediment() / static_cast(m_ulIter); + break; + + case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetFineDepth(); + break; + + case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetSandDepth(); + break; + + case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetCoarseDepth(); + break; + + case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetFineDepth(); + break; + + case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetSandDepth(); + break; + + case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetCoarseDepth(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionFine(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionSand(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionCoarse(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseFine(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseSand(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseCoarse(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseSandTalusDeposition(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseCoarseTalusDeposition(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSandTalusDeposition(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCoarseTalusDeposition(); + break; + + case (RASTER_PLOT_CLIFF_NOTCH_ALL): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->dGetCliffNotchIncisionDepth(); + break; + +#ifdef _DEBUG + case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): + dTmp = static_cast(m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->ulGetCliffCollapseTimestep()); + bIsUnsignedLong = true; + break; +#endif + + case (RASTER_PLOT_INTERVENTION_HEIGHT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetInterventionHeight(); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle(); + else + dTmp = 0; + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight(); + else + dTmp = 0; + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): + if (m_pRasterGrid->m_Cell[nX][nY].bIsInundated()) + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWavePeriod(); + else + dTmp = 0; + break; + + case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): + nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); + + if (nPoly == INT_NODATA) + dTmp = m_dMissingValue; + else + { + // Get total volume (all sediment size classes) of change in sediment for this polygon for this timestep (-ve erosion, +ve deposition) + dTmp = m_VCoast[nPolyCoast].pGetPolygon(nPoly)->dGetBeachDepositionAndSuspensionAllUncons() * m_dCellArea; + + // Calculate the rate in m^3 / sec + dTmp /= (m_dTimeStep * 3600); + } + break; + + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): + // cppcheck-suppress assignBoolToFloat + dTmp = m_pRasterGrid->m_Cell[nX][nY].bPotentialPlatformErosion(); + break; + + case (RASTER_PLOT_INUNDATION_MASK): + // cppcheck-suppress assignBoolToFloat + dTmp = m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea(); + break; + + case (RASTER_PLOT_BEACH_MASK): + dTmp = 0; + nTopLayer = m_pRasterGrid->m_Cell[nX][nY].nGetTopNonZeroLayerAboveBasement(); + + if ((nTopLayer == INT_NODATA) || (nTopLayer == NO_NONZERO_THICKNESS_LAYERS)) + break; + + if ((m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nTopLayer)->dGetAllUnconsDepth() > 0) && (m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus() > m_dThisIterSWL)) + dTmp = 1; + + break; + + case (RASTER_PLOT_SLICE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetLayerAtElev(dElev); + break; + + case (RASTER_PLOT_LANDFORM): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); + bIsInteger = true; + break; + + case (RASTER_PLOT_INTERVENTION_CLASS): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetInterventionClass(); + bIsInteger = true; + break; + + case (RASTER_PLOT_COAST): + dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline() ? 1 : 0); + break; + + case (RASTER_PLOT_NORMAL_PROFILE): + // dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsProfile() ? 1 : 0); + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID(); + bIsInteger = true; + break; + + case (RASTER_PLOT_ACTIVE_ZONE): + dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsInActiveZone() ? 1 : 0); + break; + + case (RASTER_PLOT_POLYGON): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + bIsInteger = true; + break; + + case (RASTER_PLOT_SHADOW_ZONE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber(); + bIsInteger = true; + break; + + case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); + bIsInteger = true; + break; + + case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): + nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); + bIsInteger = true; + + if (nPoly == INT_NODATA) + dTmp = m_nMissingValue; + else + { + if (m_VCoast[nPolyCoast].pGetPolygon(nPoly)->bDownCoastThisIter()) + dTmp = 1; + else + dTmp = 0; + } + break; + + case (RASTER_PLOT_SEDIMENT_INPUT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nTopLayer)->pGetUnconsolidatedSediment()->dGetTotAllSedimentInputDepth(); + break; + + case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): + dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodBySetupSurge() ? 1 : 0); + break; + + case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): + dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodBySetupSurgeRunup() ? 1 : 0); + break; + + case (RASTER_PLOT_WAVE_FLOOD_LINE): + dTmp = (m_pRasterGrid->m_Cell[nX][nY].bIsFloodline() ? 1 : 0); + break; + } + + // If necessary, scale this value + if (bScaleOutput) + { + if (bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) + dTmp = 0; // TODO 032 Improve this + else + dTmp = dRound(static_cast(m_lGDALMinCanWrite) + (dRangeScale * (dTmp - dDataMin))); + } + + // Write this value to the array + pdRaster[n++] = dTmp; + } + } + + // Create a single raster band + GDALRasterBand *pBand = pDataSet->GetRasterBand(1); + + // And fill it with the NODATA value + if (bIsInteger) + pBand->Fill(m_nMissingValue); + else if (bIsUnsignedLong) + pBand->Fill(static_cast(m_ulMissingValue)); + else + pBand->Fill(m_dMissingValue); + + // Set value units for this band + string strUnits; + + switch (nDataItem) + { + case (RASTER_PLOT_ACTUAL_BEACH_EROSION): + case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): + case (RASTER_PLOT_AVG_SEA_DEPTH): + case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): + case (RASTER_PLOT_AVG_WAVE_HEIGHT): + case (RASTER_PLOT_BASEMENT_ELEVATION): + case (RASTER_PLOT_BEACH_DEPOSITION): + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): + case (RASTER_PLOT_CLIFF_NOTCH_ALL): + case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): + case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_INTERVENTION_HEIGHT): + case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): + case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): + case (RASTER_PLOT_SEA_DEPTH): + case (RASTER_PLOT_SEDIMENT_INPUT): + case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): + case (RASTER_PLOT_SUSPENDED_SEDIMENT): + case (RASTER_PLOT_TALUS): + case (RASTER_PLOT_TOP_ELEV_INC_SEA): + case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): + case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): + case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): + case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): + case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): + case (RASTER_PLOT_WAVE_HEIGHT): + strUnits = "m"; + break; + + case (RASTER_PLOT_CONS_SED_SLOPE): + strUnits = "m/m"; + break; + + strUnits = "degrees"; + case (RASTER_PLOT_AVG_WAVE_ORIENTATION): + case (RASTER_PLOT_WAVE_ORIENTATION): + break; + + case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): + strUnits = "cumecs"; + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_PERIOD): + strUnits = "secs"; + break; + + strUnits = "none"; + case (RASTER_PLOT_ACTIVE_ZONE): + case (RASTER_PLOT_BEACH_MASK): +#ifdef _DEBUG + case (RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP): +#endif + case (RASTER_PLOT_COAST): + case (RASTER_PLOT_INTERVENTION_CLASS): + case (RASTER_PLOT_INUNDATION_MASK): + case (RASTER_PLOT_LANDFORM): + case (RASTER_PLOT_NORMAL_PROFILE): + case (RASTER_PLOT_POLYGON): + case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK): + case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): + case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): + case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): + case (RASTER_PLOT_SHADOW_ZONE): + case (RASTER_PLOT_SLICE): + case (RASTER_PLOT_WAVE_FLOOD_LINE): + break; + } + + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails + pBand->SetUnitType(strUnits.c_str()); // Not supported for some GIS formats + CPLPopErrorHandler(); + + // Tell the output dataset about NODATA (missing values) + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails + + if (bIsInteger) + pBand->SetNoDataValue(m_nMissingValue); // Will fail for some formats + if (bIsUnsignedLong) + pBand->SetNoDataValueAsUInt64(m_ulMissingValue); // Will fail for some formats + else + pBand->SetNoDataValue(m_dMissingValue); // Will fail for some formats + + CPLPopErrorHandler(); + + // Construct the description + string strDesc(*strPlotTitle); + + if (nDataItem == RASTER_PLOT_SLICE) + { + ststrTmp.clear(); + ststrTmp << dElev << "m, "; + strDesc.append(ststrTmp.str()); + } + + strDesc.append(" at "); + strDesc.append(strDispTime(m_dSimElapsed, false, false)); + + // Set the GDAL description + pBand->SetDescription(strDesc.c_str()); + + // Set raster category names + char **papszCategoryNames = NULL; + + switch (nDataItem) + { + case (RASTER_PLOT_SLICE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Basement"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 0"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 1"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 2"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 3"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 4"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 5"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 6"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 7"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 8"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Layer 9"); + break; + + case (RASTER_PLOT_LANDFORM): + papszCategoryNames = CSLAddString(papszCategoryNames, "None"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Hinterland"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Sea"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Cliff"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Drift"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Intervention"); + + papszCategoryNames = CSLAddString(papszCategoryNames, "Cliff on Coastline"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Inland Cliff"); + + papszCategoryNames = CSLAddString(papszCategoryNames, "Mixed Drift"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Talus"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Beach"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Dunes"); + break; + + case (RASTER_PLOT_INTERVENTION_CLASS): + papszCategoryNames = CSLAddString(papszCategoryNames, "None"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Structural"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Non-Structural"); + break; + + case (RASTER_PLOT_COAST): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not coastline"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Coastline"); + break; + + case (RASTER_PLOT_NORMAL_PROFILE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not coastline-normal profile"); + papszCategoryNames = CSLAddString(papszCategoryNames, "Coastline-normal profile"); + break; + + case (RASTER_PLOT_ACTIVE_ZONE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not in active zone"); + papszCategoryNames = CSLAddString(papszCategoryNames, "In active zone"); + break; + + case (RASTER_PLOT_POLYGON): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not polygon"); + papszCategoryNames = CSLAddString(papszCategoryNames, "In polygon"); + break; + + case (RASTER_PLOT_SHADOW_ZONE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not in shadow zone"); + papszCategoryNames = CSLAddString(papszCategoryNames, "In shadow zone"); + break; + + case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Not in shadow downdrift zone"); + papszCategoryNames = CSLAddString(papszCategoryNames, "In shadow downdrift zone"); + break; + + case (RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT): + papszCategoryNames = CSLAddString(papszCategoryNames, "Updrift movement of unconsolidated sediment "); + papszCategoryNames = CSLAddString(papszCategoryNames, "Downdrift movement of unconsolidated sediment"); + break; + + case (RASTER_PLOT_SETUP_SURGE_FLOOD_MASK): + papszCategoryNames = CSLAddString(papszCategoryNames, "Inundated by swl setup and surge "); + papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl setup and surge"); + break; + + case (RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK): + papszCategoryNames = CSLAddString(papszCategoryNames, "Inundated by swl setup, surge and runup "); + papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl setup, surge and runup"); + break; + + case (RASTER_PLOT_WAVE_FLOOD_LINE): + papszCategoryNames = CSLAddString(papszCategoryNames, "Intersection line of inundation "); + papszCategoryNames = CSLAddString(papszCategoryNames, "Not inundated by swl waves and runup"); + break; + } + + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail silently, if it fails + pBand->SetCategoryNames(papszCategoryNames); // Not supported for some GIS formats + CPLPopErrorHandler(); + + // Now write the data with optimized I/O + // Enable multi-threaded compression for faster writing + CPLSetThreadLocalConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); + + if (CE_Failure == pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL)) + { + // Write error, better error message + cerr << ERR << "cannot write data for " << m_strRasterGISOutFormat << " file named " << strFilePathName << endl + << CPLGetLastErrorMsg() << endl; + delete[] pdRaster; + return false; + } + + // Calculate statistics for this band + double dMin, dMax, dMean, dStdDev; + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail + // silently, if it fails + pBand->ComputeStatistics(false, &dMin, &dMax, &dMean, &dStdDev, NULL, NULL); + CPLPopErrorHandler(); + + // And then write the statistics + CPLPushErrorHandler(CPLQuietErrorHandler); // Needed to get next line to fail + // silently, if it fails + pBand->SetStatistics(dMin, dMax, dMean, dStdDev); + CPLPopErrorHandler(); + + if (!m_bGDALCanCreate) { + // Since the user-selected raster driver cannot use the Create() method, we + // have been writing to a dataset created by the in-memory driver. So now we + // need to use CreateCopy() to copy this in-memory dataset to a file in the + // user-specified raster driver format + GDALDriver *pOutDriver = GetGDALDriverManager()->GetDriverByName( + m_strRasterGISOutFormat.c_str()); + GDALDataset *pOutDataSet = + pOutDriver->CreateCopy(strFilePathName.c_str(), pDataSet, false, + m_papszGDALRasterOptions, NULL, NULL); + + if (NULL == pOutDataSet) { + // Couldn't create file + cerr << ERR << "cannot create " << m_strRasterGISOutFormat + << " file named " << strFilePathName << endl + << CPLGetLastErrorMsg() << endl; + return false; + } + + // Get rid of this user-selected dataset object + GDALClose(pOutDataSet); + } + + // Get rid of dataset object + GDALClose(pDataSet); + + // Also get rid of memory allocated to this array + delete[] pdRaster; + + return true; +} + +//=============================================================================================================================== +//! Interpolates wave properties from profile points to all cells within polygons +//! +//! ALGORITHM: k-Nearest Neighbor Inverse Distance Weighting (k-NN IDW) +//! +//! This function takes wave height components (X and Y) measured at discrete profile points +//! and interpolates them to a regular grid using a spatial interpolation method. +//! +//! METHOD OVERVIEW: +//! 1. Builds a k-d tree spatial index from input profile point coordinates (X, Y) +//! 2. For each grid cell, finds the k nearest profile points (typically k=12) +//! 3. Calculates interpolated value using inverse distance weighting (IDW) with power=2 +//! 4. Converts X/Y wave components back to magnitude and direction +//! 5. Updates grid cells with interpolated wave properties +//! +//! KEY PARAMETERS (defined in spatial_interpolation.cpp DualSpatialInterpolator): +//! - k_neighbors = 12 Number of nearest points to use for interpolation +//! - power = 2.0 Exponent for inverse distance weighting +//! (higher = more weight to closer points) +//! +//! TUNING GUIDANCE: +//! - Increase k_neighbors (e.g., 15-20) for smoother results with more averaging +//! - Decrease k_neighbors (e.g., 8-10) for results that follow local variations more closely +//! - Increase power (e.g., 3.0-4.0) to emphasize nearby points (sharper transitions) +//! - Decrease power (e.g., 1.0-1.5) for smoother, more gradual transitions +//! +//! IMPORTANT NOTES: +//! - This method does NOT respect transect structure (point 1 vs point 2, etc.) +//! - Treats all input points equally regardless of which transect they belong to +//! - Works well for scattered points but may not preserve transect-aligned features +//! - For transect-aware interpolation, consider bilinear methods instead +//! +//! COASTLINE ORIENTATION: +//! - The coastlines are available in m_VCoast[] vector +//! - Each coast has flux orientation: m_VCoast[i].dGetFluxOrientation(coastpoint) +//! - Each coast has breaking wave angle: m_VCoast[i].dGetBreakingWaveAngle(coastpoint) +//! - These could be used to implement coast-aware interpolation algorithms +//! +//! @param pVTransects Vector of TransectWaveData containing wave data per transect +//! @param pVdDeepWaterX X coordinates of deep water grid edge points +//! @param pVdDeepWaterY Y coordinates of deep water grid edge points +//! @param pVdDeepWaterHeightX X component of wave height at deep water points +//! @param pVdDeepWaterHeightY Y component of wave height at deep water points +//! @return RTN_OK on success, error code otherwise +//=============================================================================================================================== +int CSimulation::nInterpolateWavesToPolygonCells( + vector const *pVTransects, + vector const *pVdDeepWaterX, + vector const *pVdDeepWaterY, + vector const *pVdDeepWaterHeightX, + vector const *pVdDeepWaterHeightY) { + + // ============================================================================ + // STEP 1: Calculate grid dimensions and initialize variables + // ============================================================================ + + int nXSize = 0; + int nYSize = 0; + + // Average values used as fallback when interpolation fails or returns NaN + double dXAvg = 0; + double dYAvg = 0; + + // Calculate bounding box size + nXSize = m_nXMaxBoundingBox - m_nXMinBoundingBox + 1; + nYSize = m_nYMaxBoundingBox - m_nYMinBoundingBox + 1; + int const nGridSize = nXSize * nYSize; + + // Count total points across all transects plus deep water points + unsigned int nPoints = 0; + for (const auto& transect : *pVTransects) { + nPoints += static_cast(transect.VdX.size()); + } + nPoints += static_cast(pVdDeepWaterX->size()); + + // Initialize output arrays (will hold interpolated X and Y wave components) + vector VdOutX(nGridSize, 0); + vector VdOutY(nGridSize, 0); + + // ============================================================================ + // STEP 2: Prepare input data for spatial interpolation + // ============================================================================ + + // Flatten transect data and deep water data into contiguous arrays for the interpolator + std::vector points; + std::vector VdHeightX; + std::vector VdHeightY; + + points.reserve(nPoints); + VdHeightX.reserve(nPoints); + VdHeightY.reserve(nPoints); + + // Add profile/transect points + for (const auto& transect : *pVTransects) { + for (size_t i = 0; i < transect.VdX.size(); i++) { + points.emplace_back(transect.VdX[i], transect.VdY[i]); + VdHeightX.push_back(transect.VdHeightX[i]); + VdHeightY.push_back(transect.VdHeightY[i]); + } + } + + // Add deep water grid edge points + for (size_t i = 0; i < pVdDeepWaterX->size(); i++) { + points.emplace_back((*pVdDeepWaterX)[i], (*pVdDeepWaterY)[i]); + VdHeightX.push_back((*pVdDeepWaterHeightX)[i]); + VdHeightY.push_back((*pVdDeepWaterHeightY)[i]); + } + + // ============================================================================ + // STEP 3: Create spatial interpolator + // ============================================================================ + // + // DualSpatialInterpolator parameters: + // - points: Input point coordinates (from profiles/transects) + // - VdHeightX, VdHeightY: Wave height X and Y components at those points + // - k_neighbors = 12: Use 12 nearest neighbors for interpolation + // ** ADJUST THIS to change smoothness vs local detail ** + // - power = 2.0: Inverse distance weighting power + // ** ADJUST THIS to change influence of nearby vs distant points ** + // + // The interpolator builds a k-d tree for fast nearest neighbor search + // and shares it between X and Y interpolation for efficiency + DualSpatialInterpolator interp(points, VdHeightX, VdHeightY, 6, 2.0); + + // ============================================================================ + // STEP 4: Build query points (grid cells where we want interpolated values) + // ============================================================================ + + std::vector query_points; + query_points.reserve(nGridSize); + for (int nY = m_nYMinBoundingBox; nY <= m_nYMaxBoundingBox; nY++) { + for (int nX = m_nXMinBoundingBox; nX <= m_nXMaxBoundingBox; nX++) { + query_points.emplace_back(static_cast(nX), + static_cast(nY)); + } + } + + // ============================================================================ + // STEP 5: Perform batch interpolation + // ============================================================================ + // + // This does the actual interpolation for all grid points at once + // Uses OpenMP parallelization if available (see spatial_interpolation.cpp) + // Interpolates both X and Y components simultaneously using shared k-d tree + interp.Interpolate(query_points, VdOutX, VdOutY); + + // ============================================================================ + // STEP 6: Validate results and calculate average values for fallback + // ============================================================================ + // + // Check for NaN or unreasonably large values and replace with missing value marker + // Also calculate average of valid values to use as fallback + + int nXValid = 0; + int nYValid = 0; + + // Validate X component + for (unsigned int n = 0; n < VdOutX.size(); n++) { + if (isnan(VdOutX[n])) + VdOutX[n] = m_dMissingValue; + else if (tAbs(VdOutX[n]) > 1e10) // Sanity check for unreasonably large values + VdOutX[n] = m_dMissingValue; + else { + dXAvg += VdOutX[n]; + nXValid++; + } + } + + // Validate Y component + for (unsigned int n = 0; n < VdOutY.size(); n++) { + if (isnan(VdOutY[n])) + VdOutY[n] = m_dMissingValue; + else if (tAbs(VdOutY[n]) > 1e10) // Sanity check for unreasonably large values + VdOutY[n] = m_dMissingValue; + else { + dYAvg += VdOutY[n]; + nYValid++; + } + } + + // Calculate averages (for use as fallback when individual cells have missing values) + if (nXValid > 0) + dXAvg /= nXValid; + if (nYValid > 0) + dYAvg /= nYValid; + + // ============================================================================ + // STEP 7: Update grid cells with interpolated wave properties + // ============================================================================ + // + // Convert X and Y components back to magnitude and direction, + // then update each cell's wave attributes + + int n = 0; + + for (int nY = 0; nY < nYSize; nY++) { + for (int nX = 0; nX < nXSize; nX++) { + int const nActualX = nX + m_nXMinBoundingBox; + int const nActualY = nY + m_nYMinBoundingBox; + + if (m_pRasterGrid->m_Cell[nActualX][nActualY] + .bIsInContiguousSea()) { + // Only update sea cells + + if (m_pRasterGrid->m_Cell[nActualX][nActualY].nGetPolygonID() == + INT_NODATA) { + // -------------------------------------------------------------------- + // Deep water cell (NOT in a polygon) + // -------------------------------------------------------------------- + // Use the cell's pre-assigned deep water wave values + // (these cells are beyond the coastal zone, so don't need interpolation) + + double const dDeepWaterWaveHeight = + m_pRasterGrid->m_Cell[nActualX][nActualY] + .dGetCellDeepWaterWaveHeight(); + m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveHeight( + dDeepWaterWaveHeight); + + double const dDeepWaterWaveAngle = + m_pRasterGrid->m_Cell[nActualX][nActualY] + .dGetCellDeepWaterWaveAngle(); + m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveAngle( + dDeepWaterWaveAngle); + } else { + // -------------------------------------------------------------------- + // Coastal zone cell (IN a polygon) + // -------------------------------------------------------------------- + // Use the interpolated wave values calculated above + + double dWaveHeightX; + double dWaveHeightY; + + // Get interpolated X component (use average as fallback if missing/invalid) + if ((isnan(VdOutX[n])) || + (bFPIsEqual(VdOutX[n], m_dMissingValue, TOLERANCE))) + dWaveHeightX = dXAvg; + else + dWaveHeightX = VdOutX[n]; + + // Get interpolated Y component (use average as fallback if missing/invalid) + if ((isnan(VdOutY[n])) || + (bFPIsEqual(VdOutY[n], m_dMissingValue, TOLERANCE))) + dWaveHeightY = dYAvg; + else + dWaveHeightY = VdOutY[n]; + + // Convert X/Y components to magnitude and direction + double const dWaveHeight = sqrt((dWaveHeightX * dWaveHeightX) + + (dWaveHeightY * dWaveHeightY)); + double const dWaveDir = + atan2(dWaveHeightX, dWaveHeightY) * (180 / PI); + + // Update the cell's wave attributes + m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveHeight( + dWaveHeight); + m_pRasterGrid->m_Cell[nActualX][nActualY].SetWaveAngle( + dKeepWithin360(dWaveDir)); + + // Calculate wave height-to-depth ratio and update active zone status + // (active zone = where waves are breaking or near-breaking) + double const dSeaDepth = + m_pRasterGrid->m_Cell[nActualX][nActualY].dGetSeaDepth(); + + if ((dWaveHeight / dSeaDepth) >= + m_dBreakingWaveHeightDepthRatio) + m_pRasterGrid->m_Cell[nActualX][nActualY].SetInActiveZone( + true); + + // LogStream << " nX = " << nX << " nY = " << nY << " [" << + // nActualX + // << "][" << nActualY << "] waveheight = " << dWaveHeight << " + // dWaveDir = " << dWaveDir << " dKeepWithin360(dWaveDir) = " << + // dKeepWithin360(dWaveDir) << endl; + } + } + + // Increment with safety check + n++; + n = tMin(n, static_cast(VdOutX.size() - 1)); + } + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! If the user supplies multiple deep water wave height and angle values, +//! this routine interplates these to all cells (including dry land cells) +//=============================================================================================================================== +int CSimulation::nInterpolateAllDeepWaterWaveValues(void) { + // Interpolate deep water height and orientation from multiple + // user-supplied values + unsigned int const nUserPoints = + static_cast(m_VdDeepWaterWaveStationX.size()); + + // Performance optimization: Enable GDAL threading for interpolation + CPLSetThreadLocalConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); + + // Call GDALGridCreate() with the GGA_InverseDistanceToAPower + // interpolation algorithm. It has following parameters: radius1 is the + // first radius (X axis if rotation angle is 0) of the search ellipse, + // set this to zero (the default) to use the whole point array; radius2 + // is the second radius (Y axis if rotation angle is 0) of the search + // ellipse, again set this parameter to zero (the default) to use the + // whole point array; angle is the angle of the search ellipse rotation + // in degrees (counter clockwise, default 0.0); nodata is the NODATA + // marker to fill empty points (default 0.0) TODO 086 + GDALGridInverseDistanceToAPowerOptions *pOptions = + new GDALGridInverseDistanceToAPowerOptions(); + pOptions->dfAngle = 0; + pOptions->dfAnisotropyAngle = 0; + pOptions->dfAnisotropyRatio = 0; + pOptions->dfPower = 2; // Reduced from 3 to 2 for faster computation + pOptions->dfSmoothing = + 50; // Reduced from 100 to 50 for faster computation + pOptions->dfRadius1 = 0; + pOptions->dfRadius2 = 0; + pOptions->nMaxPoints = + 12; // Limit points for faster computation (was 0 = unlimited) + pOptions->nMinPoints = 3; // Minimum points needed for interpolation + pOptions->dfNoDataValue = m_nMissingValue; + + // CPLSetConfigOption("CPL_DEBUG", "ON"); + // CPLSetConfigOption("GDAL_NUM_THREADS", "1"); + + // OK, now create a gridded version of wave height: first create the + // GDAL context TODO 086 GDALGridContext* pContext = + // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, + // nUserPoints, &m_VdDeepWaterWaveStationX[0], + // &m_VdDeepWaterWaveStationY[0], + // &m_VdThisIterDeepWaterWaveStationHeight[0], true); + GDALGridContext *pContext = GDALGridContextCreate( + GGA_InverseDistanceToAPower, pOptions, nUserPoints, + m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), + m_VdThisIterDeepWaterWaveStationHeight.data(), true); + + if (pContext == NULL) { + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Now process the context + double *dHeightOut = new double[m_ulNumCells]; + int nRet = GDALGridContextProcess( + pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, + m_nYGridSize, GDT_Float64, dHeightOut, NULL, NULL); + + if (nRet == CE_Failure) { + delete[] dHeightOut; + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Get rid of the context + GDALGridContextFree(pContext); + + // Next create a gridded version of wave orientation: first create the + // GDAL context pContext = + // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, + // nUserPoints, &(m_VdDeepWaterWaveStationX[0]), + // &(m_VdDeepWaterWaveStationY[0]), + // (&m_VdThisIterDeepWaterWaveStationAngle[0]), true); + pContext = GDALGridContextCreate( + GGA_InverseDistanceToAPower, pOptions, nUserPoints, + m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), + m_VdThisIterDeepWaterWaveStationAngle.data(), true); + + if (pContext == NULL) { + delete[] dHeightOut; + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Now process the context TODO 086 + double *dAngleOut = new double[m_ulNumCells]; + nRet = GDALGridContextProcess( + pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, + m_nYGridSize, GDT_Float64, dAngleOut, NULL, NULL); + + if (nRet == CE_Failure) { + delete[] dHeightOut; + delete[] dAngleOut; + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Get rid of the context + GDALGridContextFree(pContext); + + // OK, now create a gridded version of wave period: first create the + // GDAL context pContext = + // GDALGridContextCreate(GGA_InverseDistanceToAPower, pOptions, + // nUserPoints, &m_VdDeepWaterWaveStationX[0], + // &m_VdDeepWaterWaveStationY[0], + // &m_VdThisIterDeepWaterWaveStationPeriod[0], true); + pContext = GDALGridContextCreate( + GGA_InverseDistanceToAPower, pOptions, nUserPoints, + m_VdDeepWaterWaveStationX.data(), m_VdDeepWaterWaveStationY.data(), + m_VdThisIterDeepWaterWaveStationPeriod.data(), true); + + if (pContext == NULL) { + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Now process the context TODO 086 + double *dPeriopdOut = new double[m_ulNumCells]; + nRet = GDALGridContextProcess( + pContext, 0, m_nXGridSize - 1, 0, m_nYGridSize - 1, m_nXGridSize, + m_nYGridSize, GDT_Float64, dPeriopdOut, NULL, NULL); + + if (nRet == CE_Failure) { + delete[] dPeriopdOut; + delete pOptions; + return RTN_ERR_GRIDCREATE; + } + + // Get rid of the context + GDALGridContextFree(pContext); + + // The output from GDALGridCreate() is in dHeightOut, dAngleOut and + // dPeriopdOut but must be reversed + vector VdHeight; + vector VdAngle; + vector VdPeriod; + + int n = 0; + int nValidHeight = 0; + int nValidAngle = 0; + int nValidPeriod = 0; + + double dAvgHeight = 0; + double dAvgAngle = 0; + double dAvgPeriod = 0; + + for (int nY = m_nYGridSize - 1; nY >= 0; nY--) { + for (int nX = 0; nX < m_nXGridSize; nX++) { + if (isfinite(dHeightOut[n])) { + VdHeight.push_back(dHeightOut[n]); + + dAvgHeight += dHeightOut[n]; + nValidHeight++; + } + + else { + VdHeight.push_back(m_dMissingValue); + } + + if (isfinite(dAngleOut[n])) { + VdAngle.push_back(dAngleOut[n]); + + dAvgAngle += dAngleOut[n]; + nValidAngle++; + } + + else { + VdAngle.push_back(m_dMissingValue); + } + + if (isfinite(dPeriopdOut[n])) { + VdPeriod.push_back(dPeriopdOut[n]); + + dAvgPeriod += dPeriopdOut[n]; + nValidPeriod++; + } + + else { + VdPeriod.push_back(m_dMissingValue); + } + + // LogStream << " nX = " << nX << " nY = " << nY << " n = " << n << + // " dHeightOut[n] = " << dHeightOut[n] << " dAngleOut[n] = " << + // dAngleOut[n] << endl; + n++; + } + } + + // Calculate averages + dAvgHeight /= nValidHeight; + dAvgAngle /= nValidAngle; + dAvgPeriod /= nValidPeriod; + + // Tidy + delete pOptions; + delete[] dHeightOut; + delete[] dAngleOut; + delete[] dPeriopdOut; + + // Now update all raster cells + n = 0; + + for (int nY = 0; nY < m_nYGridSize; nY++) { + for (int nX = 0; nX < m_nXGridSize; nX++) { + if (bFPIsEqual(VdHeight[n], m_dMissingValue, TOLERANCE)) + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveHeight( + dAvgHeight); + + else + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveHeight( + VdHeight[n]); + + if (bFPIsEqual(VdAngle[n], m_dMissingValue, TOLERANCE)) + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveAngle( + dAvgAngle); + + else + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWaveAngle( + VdAngle[n]); + + if (bFPIsEqual(VdPeriod[n], m_dMissingValue, TOLERANCE)) + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWavePeriod( + dAvgPeriod); + + else + m_pRasterGrid->m_Cell[nX][nY].SetCellDeepWaterWavePeriod( + VdPeriod[n]); + + // LogStream << " [" << nX << "][" << nY << "] deep water wave + // height = " + // << m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight() << + // " deep water wave angle = " << + // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle() << + // endl; + n++; + } + } + + // // DEBUG CODE + // =========================================================================================================== + // string strOutFile = m_strOutPath; + // strOutFile += "init_deep_water_wave_height_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // GDALDriver* pDriver = + // GetGDALDriverManager()->GetDriverByName("gtiff"); GDALDataset* + // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, + // m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // double* pdRaster = new double[m_ulNumCells]; + // int nn = 0; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // // Write this value to the array + // pdRaster[nn] = + // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight(); nn++; + // } + // } + // + // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_nMissingValue); + // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, + // pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // // DEBUG CODE + // =========================================================================================================== + + // // DEBUG CODE + // =========================================================================================================== + // strOutFile = m_strOutPath; + // strOutFile += "init_deep_water_wave_angle_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, + // m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // nn = 0; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // // Write this value to the array + // pdRaster[nn] = + // m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveAngle(); nn++; + // } + // } + // + // pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_nMissingValue); + // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, + // pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // // DEBUG CODE + // =========================================================================================================== + + return RTN_OK; + } diff --git a/src/gis_utils.cpp b/src/gis_utils.cpp index b36b83a17..265d53f11 100644 --- a/src/gis_utils.cpp +++ b/src/gis_utils.cpp @@ -1,1991 +1,1991 @@ -/*! - \file gis_utils.cpp - \brief Various GIS-related functions, requires GDAL - \details Note re. coordinate systems used - - 1. In the raster CRS, cell[0][0] is at the top left (NW) corner of the grid. Raster grid co-oordinate [0][0] is actually the top left (NW) corner of this cell. - - 2. We assume that the grid CRS and external CRS have parallel axes. If they have not, see http://www.gdal.org/classGDALDataset.html which says that: - - To convert between pixel/line (P,L) raster space, and projection coordinates (Xp,Yp) space Xp = padfTransform[0] + padfTransform[1] + padfTransform[2]; Yp - = padfTransform[3] + padfTransform[4] + padfTransform[5]; - - In a north-up image, padfTransform[1] is the pixel width, and padfTransform[5] is the pixel height. The upper left corner of the upper left pixel is at position (padfTransform[0], padfTransform[3]). - - 3. Usually, raster grid CRS values are integer, i.e. they refer to a point which is at the centroid of a cell. They may also be -ve or greater than m_nXGridSize-1 i.e. may refer to a point which lies outside any cell of the raster grid. - - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include - -#include - -#include -using std::vector; - -#include -using std::cerr; -using std::endl; -using std::ios; - -#include -using std::atan2; - -#include -#include -#include - -#include -using std::strstr; - -#include -#include -#include - -#include "cme.h" -#include "coast.h" -#include "raster_grid.h" -#include "2d_point.h" -#include "2di_point.h" - -//=============================================================================================================================== -//! Given the integer X-axis ordinate of a cell in the raster grid CRS, returns the external CRS X-axis ordinate of the cell's centroid -//=============================================================================================================================== -double CSimulation::dGridCentroidXToExtCRSX(int const nGridX) const -{ - // TODO 064 - return (m_dGeoTransform[0] + (nGridX * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2)); -} - -//=============================================================================================================================== -//! Given the integer Y-axis ordinate of a cell in the raster grid CRS, returns the external CRS Y-axis ordinate of the cell's centroid -//=============================================================================================================================== -double CSimulation::dGridCentroidYToExtCRSY(int const nGridY) const -{ - // TODO 064 - return (m_dGeoTransform[3] + (nGridY * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2)); -} - -//=============================================================================================================================== -//! Transforms a pointer to a CGeom2DIPoint in the raster grid CRS (assumed to be the centroid of a cell) to the equivalent CGeom2DPoint in the external CRS -//=============================================================================================================================== -CGeom2DPoint CSimulation::PtGridCentroidToExt(CGeom2DIPoint const *pPtiIn) const -{ - // TODO 064 - double const dX = m_dGeoTransform[0] + (pPtiIn->nGetX() * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2); - double const dY = m_dGeoTransform[3] + (pPtiIn->nGetY() * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2); - - return CGeom2DPoint(dX, dY); -} - -//=============================================================================================================================== -//! Given a real-valued X-axis ordinate in the raster grid CRS (i.e. not the centroid of a cell), returns the external CRS X-axis ordinate -//=============================================================================================================================== -double CSimulation::dGridXToExtCRSX(double const dGridX) const -{ - // TODO 064 Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2) - return m_dGeoTransform[0] + (dGridX * m_dGeoTransform[1]) - 1; -} - -//=============================================================================================================================== -//! Given a real-valued Y-axis ordinate in the raster grid CRS (i.e. not the centroid of a cell), returns the external CRS Y-axis ordinate -//=============================================================================================================================== -double CSimulation::dGridYToExtCRSY(double const dGridY) const -{ - // TODO 064 Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5) - return m_dGeoTransform[3] + (dGridY * m_dGeoTransform[5]) - 1; -} - -//=============================================================================================================================== -//! Transforms an X-axis ordinate in the external CRS to the equivalent X-axis ordinate in the raster grid CRS (the result is not rounded, and so may not be integer, and may be outside the grid) -//=============================================================================================================================== -double CSimulation::dExtCRSXToGridX(double const dExtCRSX) const -{ - // TODO 064 - return ((dExtCRSX - m_dGeoTransform[0]) / m_dGeoTransform[1]) - 1; -} - -//=============================================================================================================================== -//! Transforms a Y-axis ordinate in the external CRS to the equivalent Y-axis ordinate in the raster grid CRS (the result is not rounded, and so may not be integer, and may be outside the grid) -//=============================================================================================================================== -double CSimulation::dExtCRSYToGridY(double const dExtCRSY) const -{ - // TODO 064 - return ((dExtCRSY - m_dGeoTransform[3]) / m_dGeoTransform[5]) - 1; -} - -//=============================================================================================================================== -//! Transforms a pointer to a CGeom2DPoint in the external CRS to the equivalent CGeom2DIPoint in the raster grid CRS (both values rounded). The result may be outside the grid -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiExtCRSToGridRound(CGeom2DPoint const* pPtIn) const -{ - // TODO 064 - int const nX = nRound(((pPtIn->dGetX() - m_dGeoTransform[0]) / m_dGeoTransform[1]) - 1); - int const nY = nRound(((pPtIn->dGetY() - m_dGeoTransform[3]) / m_dGeoTransform[5]) - 1); - - return CGeom2DIPoint(nX, nY); -} - -//=============================================================================================================================== -//! Returns the distance (in external CRS) between two points -//=============================================================================================================================== -double CSimulation::dGetDistanceBetween(CGeom2DPoint const* Pt1, CGeom2DPoint const* Pt2) -{ - double const dXDist = Pt1->dGetX() - Pt2->dGetX(); - double const dYDist = Pt1->dGetY() - Pt2->dGetY(); - - return hypot(dXDist, dYDist); -} - -//=============================================================================================================================== -//! Returns the distance (in external CRS) between two points -//=============================================================================================================================== -double CSimulation::dGetDistanceBetween(CGeom2DIPoint const* Pti1, CGeom2DIPoint const* Pti2) -{ - double const dXDist = Pti1->nGetX() - Pti2->nGetX(); - double const dYDist = Pti1->nGetY() - Pti2->nGetY(); - - return hypot(dXDist, dYDist); -} - -//=============================================================================================================================== -//! Returns the distance (in external CRS) between two points -//=============================================================================================================================== -double CSimulation::dGetDistanceBetween(double const dX1, double const dY1, double const dX2, double const dY2) -{ - double const dXDist = dX1 - dX2; - double const dYDist = dY1 - dY2; - - return hypot(dXDist, dYDist); -} - -//=============================================================================================================================== -//! Returns twice the signed area of a triangle, defined by three points -//=============================================================================================================================== -double CSimulation::dTriangleAreax2(CGeom2DPoint const* pPtA, CGeom2DPoint const* pPtB, CGeom2DPoint const* pPtC) -{ - return (pPtB->dGetX() - pPtA->dGetX()) * (pPtC->dGetY() - pPtA->dGetY()) - (pPtB->dGetY() - pPtA->dGetY()) * (pPtC->dGetX() - pPtA->dGetX()); -} - -//=============================================================================================================================== -//! Checks whether the supplied point (an x-y pair, in the grid CRS) is within the raster grid, and is a valid cell (i.e. the basement DEM is not NODATA) -//=============================================================================================================================== -bool CSimulation::bIsWithinValidGrid(int const nX, int const nY) const -{ - if ((nX < 0) || (nX >= m_nXGridSize)) - return false; - - if ((nY < 0) || (nY >= m_nYGridSize)) - return false; - - if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) - return false; - - return true; -} - -//=============================================================================================================================== -//! Checks whether the supplied point (a reference to a CGeom2DIPoint, in the grid CRS) is within the raster grid, and is a valid cell (i.e. the basement DEM is not NODATA) -//=============================================================================================================================== -bool CSimulation::bIsWithinValidGrid(CGeom2DIPoint const* Pti) const -{ - int const nX = Pti->nGetX(); - int const nY = Pti->nGetY(); - - return this->bIsWithinValidGrid(nX, nY); -} - -//=============================================================================================================================== -//! Constrains the supplied point (in the grid CRS) to be a valid cell within the raster grid -//=============================================================================================================================== -void CSimulation::KeepWithinValidGrid(int& nX, int& nY) const -{ - nX = tMax(nX, 0); - nX = tMin(nX, m_nXGridSize - 1); - - nY = tMax(nY, 0); - nY = tMin(nY, m_nYGridSize - 1); -} - -//=============================================================================================================================== -//! Constrains the second supplied point (both are CGeom2DIPoints, in the grid CRS) to be a valid cell within the raster grid -//=============================================================================================================================== -void CSimulation::KeepWithinValidGrid(CGeom2DIPoint const* Pti0, CGeom2DIPoint* Pti1) const -{ - KeepWithinValidGrid(Pti0->nGetX(), Pti0->nGetY(), *Pti1->pnGetX(), *Pti1->pnGetY()); -} - -//=============================================================================================================================== -//! Given two points in the grid CRS (the points assumed not to be coincident), this routine modifies the value of the second point so that it is on a line joining the original two points and is a valid cell within the raster grid. However in some cases (e.g. if the first point is at the edge of the valid part of the raster grid) then the second cell will be coincident with the -//! first cell, and the line joining them is thus of zero length. The calling routine has to be able to handle this -//=============================================================================================================================== -void CSimulation::KeepWithinValidGrid(int nX0, int nY0, int& nX1, int& nY1) const -{ - // Safety check: make sure that the first point is within the valid grid - if (nX0 >= m_nXGridSize) - nX0 = m_nXGridSize - 1; - - else if (nX0 < 0) - nX0 = 0; - - if (nY0 >= m_nYGridSize) - nY0 = m_nYGridSize - 1; - - else if (nY0 < 0) - nY0 = 0; - - // OK let's go - int const nDiffX = nX0 - nX1; - int const nDiffY = nY0 - nY1; - - if (nDiffX == 0) - { - // The two points have the same x coordinates, so we just need to constrain the y co-ord - if (nY1 < nY0) - { - nY1 = -1; - - do - { - nY1++; - } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); - - return; - } - else - { - nY1 = m_nYGridSize; - - do - { - nY1--; - } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); - - return; - } - } - else if (nDiffY == 0) - { - // The two points have the same y coordinates, so we just need to constrain the x co-ord - if (nX1 < nX0) - { - nX1 = -1; - - do - { - nX1++; - } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); - - return; - } - else - { - nX1 = m_nXGridSize; - - do - { - nX1--; - } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); - - return; - } - } - else - { - // The two points have different x coordinates and different y coordinates, so we have to work harder. First find which of the coordinates is the greatest distance outside the grid, and constrain that co-ord for efficiency (since this will reduce the number of times round the loop). Note that both may be inside the grid, if the incorrect co-ord is in the invalid margin, in which case arbitrarily contrain the x co-ord - int nXDistanceOutside = 0; - int nYDistanceOutside = 0; - - if (nX1 < 0) - nXDistanceOutside = -nX1; - else if (nX1 >= m_nXGridSize) - nXDistanceOutside = nX1 - m_nXGridSize + 1; - - if (nY1 < 0) - nYDistanceOutside = -nY1; - else if (nY1 >= m_nYGridSize) - nXDistanceOutside = nY1 - m_nYGridSize + 1; - - if (nXDistanceOutside >= nYDistanceOutside) - { - // Constrain the x co-ord - if (nX1 < nX0) - { - // The incorrect x co-ord is less than the correct x co-ord: constrain it and find the y co-ord - nX1 = -1; - - do - { - nX1++; - - nY1 = nY0 + nRound(((nX1 - nX0) * nDiffY) / static_cast(nDiffX)); - } while ((nY1 < 0) || (nY1 >= m_nYGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); - - return; - } - else - { - // The incorrect x co-ord is greater than the correct x-co-ord: constrain it and find the y co-ord - nX1 = m_nXGridSize; - - do - { - nX1--; - - nY1 = nY0 + nRound(((nX1 - nX0) * nDiffY) / static_cast(nDiffX)); - } while ((nY1 < 0) || (nY1 >= m_nYGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); - - return; - } - } - else - { - // Constrain the y co-ord - if (nY1 < nY0) - { - // The incorrect y co-ord is less than the correct y-co-ord: constrain it and find the x co-ord - nY1 = -1; - - do - { - nY1++; - - nX1 = nX0 + nRound(((nY1 - nY0) * nDiffX) / static_cast(nDiffY)); - } while ((nX1 < 0) || (nX1 >= m_nXGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); - - return; - } - else - { - // The incorrect y co-ord is greater than the correct y co-ord: constrain it and find the x co-ord - nY1 = m_nYGridSize; - - do - { - nY1--; - - nX1 = nX0 + - nRound(((nY1 - nY0) * nDiffX) / static_cast(nDiffY)); - } while ((nX1 < 0) || (nX1 >= m_nXGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); - - return; - } - } - } -} - -//=============================================================================================================================== -//! Constrains the supplied angle to be within 0 and 360 degrees -//=============================================================================================================================== -double CSimulation::dKeepWithin360(double const dAngle) -{ - double dNewAngle = dAngle; - - // Sort out -ve angles - while (dNewAngle < 0) - dNewAngle += 360; - - // Sort out angles > 360 - while (dNewAngle > 360) - dNewAngle -= 360; - - return dNewAngle; -} - -//=============================================================================================================================== -//! Returns a point (external CRS) which is the average of (i.e. is midway between) two other external CRS points -//=============================================================================================================================== -CGeom2DPoint CSimulation::PtAverage(CGeom2DPoint const* pPt1, CGeom2DPoint const* pPt2) -{ - double const dPt1X = pPt1->dGetX(); - double const dPt1Y = pPt1->dGetY(); - double const dPt2X = pPt2->dGetX(); - double const dPt2Y = pPt2->dGetY(); - double const dPtAvgX = (dPt1X + dPt2X) / 2; - double const dPtAvgY = (dPt1Y + dPt2Y) / 2; - - return CGeom2DPoint(dPtAvgX, dPtAvgY); -} - -// //=============================================================================================================================== -// //! Returns an integer point (grid CRS) which is the approximate average of (i.e. is midway between) two other grid CRS integer points -// //=============================================================================================================================== -// CGeom2DIPoint CSimulation::PtiAverage(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2) -// { -// int const nPti1X = pPti1->nGetX(); -// int const nPti1Y = pPti1->nGetY(); -// int const nPti2X = pPti2->nGetX(); -// int const nPti2Y = pPti2->nGetY(); -// int const nPtiAvgX = (nPti1X + nPti2X) / 2; -// int const nPtiAvgY = (nPti1Y + nPti2Y) / 2; -// -// return CGeom2DIPoint(nPtiAvgX, nPtiAvgY); -// } - -//=============================================================================================================================== -//! Returns an integer point (grid CRS) which is the weighted average of two other grid CRS integer points. The weight must be <= 1, if the weight is < 0.5 then the output point is closer to the first point, if the weight is > 0.5 then the output point is closer to the second point -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiWeightedAverage(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2, double const dWeight) -{ - int const nPti1X = pPti1->nGetX(); - int const nPti1Y = pPti1->nGetY(); - int const nPti2X = pPti2->nGetX(); - int const nPti2Y = pPti2->nGetY(); - double const dOtherWeight = 1.0 - dWeight; - - int const nPtiWeightAvgX = nRound((dWeight * nPti2X) + (dOtherWeight * nPti1X)); - int const nPtiWeightAvgY = nRound((dWeight * nPti2Y) + (dOtherWeight * nPti1Y)); - - return CGeom2DIPoint(nPtiWeightAvgX, nPtiWeightAvgY); -} - -//=============================================================================================================================== -//! Returns a point (external CRS) which is the average of a vector of external CRS points -//=============================================================================================================================== -CGeom2DPoint CSimulation::PtAverage(vector* pVIn) -{ - int const nSize = static_cast(pVIn->size()); - - if (nSize == 0) - return CGeom2DPoint(DBL_NODATA, DBL_NODATA); - - double dAvgX = 0; - double dAvgY = 0; - - for (int n = 0; n < nSize; n++) - { - dAvgX += pVIn->at(n).dGetX(); - dAvgY += pVIn->at(n).dGetY(); - } - - dAvgX /= nSize; - dAvgY /= nSize; - - return CGeom2DPoint(dAvgX, dAvgY); -} - -// //=============================================================================================================================== -// //! Returns a point (grid CRS) which is the average of a vector of grid CRS points -// //=============================================================================================================================== -// CGeom2DIPoint CSimulation::PtiAverage(vector* pVIn) -// { -// int nSize = static_cast(pVIn->size()); -// if (nSize == 0) -// return CGeom2DIPoint(INT_NODATA, INT_NODATA); -// -// double dAvgX = 0; -// double dAvgY = 0; -// -// for (int n = 0; n < nSize; n++) -// { -// dAvgX += pVIn->at(n).nGetX(); -// dAvgY += pVIn->at(n).nGetY(); -// } -// -// dAvgX /= nSize; -// dAvgY /= nSize; -// -// return CGeom2DIPoint(nRound(dAvgX), nRound(dAvgY)); -// } - -//=============================================================================================================================== -//! Returns an integer point (grid CRS) which is the centroid of a polygon, given by a vector of grid CRS points. From https://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiPolygonCentroid(vector* pVIn) -{ - CGeom2DIPoint PtiCentroid(0, 0); - int const nSize = static_cast(pVIn->size()); - int nX0 = 0; // Current vertex X - int nY0 = 0; // Current vertex Y - int nX1 = 0; // Next vertex X - int nY1 = 0; // Next vertex Y - - double dA = 0; // Partial signed area - double dSignedArea = 0.0; - - // For all vertices except last - for (int i = 0; i < nSize - 1; ++i) - { - nX0 = pVIn->at(i).nGetX(); - nY0 = pVIn->at(i).nGetY(); - nX1 = pVIn->at(i + 1).nGetX(); - nY1 = pVIn->at(i + 1).nGetY(); - - dA = (nX0 * nY1) - (nX1 * nY0); - dSignedArea += dA; - PtiCentroid.AddXAddY((nX0 + nX1) * dA, (nY0 + nY1) * dA); - } - - // Do last vertex separately to avoid performing an expensive modulus operation in each iteration - nX0 = pVIn->at(nSize - 1).nGetX(); - nY0 = pVIn->at(nSize - 1).nGetY(); - nX1 = pVIn->at(0).nGetX(); - nY1 = pVIn->at(0).nGetY(); - - dA = (nX0 * nY1) - (nX1 * nY0); - dSignedArea += dA; - PtiCentroid.AddXAddY((nX0 + nX1) * dA, (nY0 + nY1) * dA); - - dSignedArea *= 0.5; - PtiCentroid.DivXDivY(6.0 * dSignedArea, 6.0 * dSignedArea); - - return PtiCentroid; -} - -//=============================================================================================================================== -// Returns a vector which is perpendicular to an existing vector -//=============================================================================================================================== -// vector CSimulation::VGetPerpendicular(CGeom2DPoint const* -// PtStart, CGeom2DPoint const* PtNext, double const dDesiredLength, int const -// nHandedness) -// { -// // Returns a two-point vector which passes through PtStart with a scaled -// length -// double dXLen = PtNext->dGetX() - PtStart->dGetX(); -// double dYLen = PtNext->dGetY() - PtStart->dGetY(); -// -// double dLength = hypot(dXLen, dYLen); -// double dScaleFactor = dDesiredLength / dLength; -// -// // The difference vector is (dXLen, dYLen), so the perpendicular -// difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) -// CGeom2DPoint EndPt; -// if (nHandedness == RIGHT_HANDED) -// { -// EndPt.SetX(PtStart->dGetX() + (dScaleFactor * dYLen)); -// EndPt.SetY(PtStart->dGetY() - (dScaleFactor * dXLen)); -// } -// else -// { -// EndPt.SetX(PtStart->dGetX() - (dScaleFactor * dYLen)); -// EndPt.SetY(PtStart->dGetY() + (dScaleFactor * dXLen)); -// } -// -// vector VNew; -// VNew.push_back(*PtStart); -// VNew.push_back(EndPt); -// return VNew; -// } - -// //=============================================================================================================================== -// //! Returns a CGeom2DPoint which is the 'other' point of a two-point vector passing through PtStart, and which is perpendicular to the two-point vector from PtStart to PtNext -// //=============================================================================================================================== -// CGeom2DPoint CSimulation::PtGetPerpendicular(CGeom2DPoint const* PtStart, CGeom2DPoint const* PtNext, double const dDesiredLength, int const nHandedness) -// { -// double const dXLen = PtNext->dGetX() - PtStart->dGetX(); -// double const dYLen = PtNext->dGetY() - PtStart->dGetY(); -// double dLength; -// -// if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) -// dLength = dYLen; -// else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) -// dLength = dXLen; -// else -// dLength = hypot(dXLen, dYLen); -// -// double const dScaleFactor = dDesiredLength / dLength; -// -// // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) -// CGeom2DPoint EndPt; -// -// if (nHandedness == RIGHT_HANDED) -// { -// EndPt.SetX(PtStart->dGetX() + (dScaleFactor * dYLen)); -// EndPt.SetY(PtStart->dGetY() - (dScaleFactor * dXLen)); -// } -// else -// { -// EndPt.SetX(PtStart->dGetX() - (dScaleFactor * dYLen)); -// EndPt.SetY(PtStart->dGetY() + (dScaleFactor * dXLen)); -// } -// -// return EndPt; -// } - -//=============================================================================================================================== -//! Returns a CGeom2DIPoint (grid CRS) which is the 'other' point of a two-point vector passing through PtiStart, and which is perpendicular to the two-point vector from PtiStart to PtiNext -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiGetPerpendicular(CGeom2DIPoint const* PtiStart, CGeom2DIPoint const* PtiNext, double const dDesiredLength, int const nHandedness) -{ - double const dXLen = PtiNext->nGetX() - PtiStart->nGetX(); - double const dYLen = PtiNext->nGetY() - PtiStart->nGetY(); - double dLength; - - if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) - dLength = dYLen; - - else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) - dLength = dXLen; - - else - dLength = hypot(dXLen, dYLen); - - double const dScaleFactor = dDesiredLength / dLength; - - // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) - CGeom2DIPoint EndPti; - - if (nHandedness == RIGHT_HANDED) - { - EndPti.SetX(PtiStart->nGetX() + nRound(dScaleFactor * dYLen)); - EndPti.SetY(PtiStart->nGetY() - nRound(dScaleFactor * dXLen)); - } - - else - { - EndPti.SetX(PtiStart->nGetX() - nRound(dScaleFactor * dYLen)); - EndPti.SetY(PtiStart->nGetY() + nRound(dScaleFactor * dXLen)); - } - - return EndPti; -} - -//=============================================================================================================================== -//! Returns a CGeom2DIPoint (grid CRS) which is the 'other' point of a two-point vector passing through [nStartX][nStartY], and which is perpendicular to the two-point vector from [nStartX][nStartY] to [nNextX][nNextY] -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiGetPerpendicular(int const nStartX, int const nStartY, int const nNextX, int const nNextY, double const dDesiredLength, int const nHandedness) -{ - double const dXLen = nNextX - nStartX; - double const dYLen = nNextY - nStartY; - double dLength; - - if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) - dLength = dYLen; - - else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) - dLength = dXLen; - - else - dLength = hypot(dXLen, dYLen); - - double const dScaleFactor = dDesiredLength / dLength; - - // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) - CGeom2DIPoint EndPti; - - if (nHandedness == RIGHT_HANDED) - { - EndPti.SetX(nStartX + nRound(dScaleFactor * dYLen)); - EndPti.SetY(nStartY - nRound(dScaleFactor * dXLen)); - } - - else - { - EndPti.SetX(nStartX - nRound(dScaleFactor * dYLen)); - EndPti.SetY(nStartY + nRound(dScaleFactor * dXLen)); - } - - return EndPti; -} - -//=============================================================================================================================== -//! Returns the signed angle BAC (in radians) subtended between three CGeom2DIPoints B A C. From http://stackoverflow.com/questions/3057448/angle-between-3-vertices -//=============================================================================================================================== -double CSimulation::dAngleSubtended(CGeom2DIPoint const* pPtiA, CGeom2DIPoint const* pPtiB, CGeom2DIPoint const* pPtiC) -{ - double const dXDistBtoA = pPtiB->nGetX() - pPtiA->nGetX(); - double const dYDistBtoA = pPtiB->nGetY() - pPtiA->nGetY(); - double const dXDistCtoA = pPtiC->nGetX() - pPtiA->nGetX(); - double const dYDistCtoA = pPtiC->nGetY() - pPtiA->nGetY(); - double const dDotProduct = dXDistBtoA * dXDistCtoA + dYDistBtoA * dYDistCtoA; - double const dPseudoCrossProduct = dXDistBtoA * dYDistCtoA - dYDistBtoA * dXDistCtoA; - double const dAngle = atan2(dPseudoCrossProduct, dDotProduct); - - return dAngle; -} - -//=============================================================================================================================== -//! Checks whether the selected raster GDAL driver supports file creation, 32-bit doubles, etc. -//=============================================================================================================================== -bool CSimulation::bCheckRasterGISOutputFormat(void) -{ - // Register all available GDAL raster and vector drivers (GDAL 2) - GDALAllRegister(); - - // If the user hasn't specified a GIS output format, assume that we will use the same GIS format as the input basement DEM - if (m_strRasterGISOutFormat.empty()) - m_strRasterGISOutFormat = m_strGDALBasementDEMDriverCode; - - // Load the raster GDAL driver - GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName(m_strRasterGISOutFormat.c_str()); - - if (NULL == pDriver) - { - // Can't load raster GDAL driver. Incorrectly specified? - cerr << ERR << "Unknown raster GIS output format '" << m_strRasterGISOutFormat << "'." << endl; - return false; - } - - // Get the metadata for this raster driver - char **papszMetadata = pDriver->GetMetadata(); - - // for (int i = 0; papszMetadata[i] != NULL; i++) - // cout << papszMetadata[i] << endl; - // cout << endl; - - // Need to test if this is a raster driver - if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_RASTER, false)) - { - // This is not a raster driver - cerr << ERR << "GDAL driver '" << m_strRasterGISOutFormat << "' is not a raster driver. Choose another format." << endl; - return false; - } - - // This driver is OK, so store its longname and the default file extension - string strTmp = CSLFetchNameValue(papszMetadata, "DMD_LONGNAME"); - m_strGDALRasterOutputDriverLongname = strTrim(&strTmp); - strTmp = CSLFetchNameValue(papszMetadata, "DMD_EXTENSIONS"); // Note DMD_EXTENSION (no S, is a single value) appears not to be implemented for newer drivers - strTmp = strTrim(&strTmp); - - // We have a space-separated list of one or more file extensions: use the first extension in the list - long unsigned int const nPos = strTmp.find(SPACE); - - if (nPos == string::npos) - { - // No space i.e. just one extension - m_strGDALRasterOutputDriverExtension = strTmp; - } - - else - { - // There's a space, so we must have more than one extension - m_strGDALRasterOutputDriverExtension = strTmp.substr(0, nPos); - } - - // Set up any defaults for raster files that are created using this driver - SetRasterFileCreationDefaults(); - - // Now do various tests of the driver's capabilities - if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATE, false)) - { - // This raster driver does not support the Create() method, does it support CreateCopy()? - if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATECOPY, false)) - { - cerr << ERR << "Cannot write using raster GDAL driver '" << m_strRasterGISOutFormat << " since neither Create() or CreateCopy() are supported'. Choose another GDAL raster format." << endl; - return false; - } - - // Can't use Create() but can use CreateCopy() - m_bGDALCanCreate = false; - } - - // Next, test to see what data types the driver can write and from this, work out the largest int and float we can write - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Float")) - { - m_bGDALCanWriteFloat = true; - m_GDALWriteFloatDataType = GDT_Float32; - } - - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "UInt32")) - { - m_bGDALCanWriteInt32 = true; - - m_GDALWriteIntDataType = GDT_UInt32; - m_lGDALMaxCanWrite = UINT32_MAX; - m_lGDALMinCanWrite = 0; - - if (! m_bGDALCanWriteFloat) - m_GDALWriteFloatDataType = GDT_UInt32; - - return true; - } - - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Int32")) - { - m_bGDALCanWriteInt32 = true; - - m_GDALWriteIntDataType = GDT_Int32; - m_lGDALMaxCanWrite = INT32_MAX; - m_lGDALMinCanWrite = INT32_MIN; - - if (! m_bGDALCanWriteFloat) - m_GDALWriteFloatDataType = GDT_Int32; - - return true; - } - - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "UInt16")) - { - m_bGDALCanWriteInt32 = false; - - m_GDALWriteIntDataType = GDT_UInt16; - m_lGDALMaxCanWrite = UINT16_MAX; - m_lGDALMinCanWrite = 0; - - if (! m_bGDALCanWriteFloat) - m_GDALWriteFloatDataType = GDT_UInt16; - - return true; - } - - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Int16")) - { - m_bGDALCanWriteInt32 = false; - - m_GDALWriteIntDataType = GDT_Int16; - m_lGDALMaxCanWrite = INT16_MAX; - m_lGDALMinCanWrite = INT16_MIN; - - if (! m_bGDALCanWriteFloat) - m_GDALWriteFloatDataType = GDT_Int16; - - return true; - } - - if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Byte")) - { - m_bGDALCanWriteInt32 = false; - - m_GDALWriteIntDataType = GDT_Byte; - m_lGDALMaxCanWrite = UINT8_MAX; - m_lGDALMinCanWrite = 0; - - if (! m_bGDALCanWriteFloat) - m_GDALWriteFloatDataType = GDT_Byte; - - return true; - } - - // This driver does not even support byte output - cerr << ERR << "Cannot write using raster GDAL driver '" << m_strRasterGISOutFormat << ", not even byte output is supported'. Choose another GIS raster format." << endl; - return false; -} - -//=============================================================================================================================== -//! Checks whether the selected vector GDAL/OGR driver supports file creation etc. -//=============================================================================================================================== -bool CSimulation::bCheckVectorGISOutputFormat(void) -{ - // Load the vector GDAL driver (this assumes that GDALAllRegister() has already been called) - GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName(m_strVectorGISOutFormat.c_str()); - - if (NULL == pDriver) - { - // Can't load vector GDAL driver. Incorrectly specified? - cerr << ERR << "Unknown vector GIS output format '" << m_strVectorGISOutFormat << "'." << endl; - return false; - } - - // Get the metadata for this vector driver - char **papszMetadata = pDriver->GetMetadata(); - - // For GDAL2, need to test if this is a vector driver - if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_VECTOR, false)) - { - // This is not a vector driver - cerr << ERR << "GDAL driver '" << m_strVectorGISOutFormat << "' is not a vector driver. Choose another format." << endl; - return false; - } - - if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATE, false)) - { - // Driver does not support create() method - cerr << ERR << "Cannot write vector GIS files using GDAL driver '" << m_strRasterGISOutFormat << "'. Choose another format." << endl; - return false; - } - - // Driver is OK, now set some options for individual drivers - if (m_strVectorGISOutFormat == "ESRI Shapefile") - { - // Set this, so that just a single dataset-with-one-layer shapefile is created, rather than a directory (see http://www.gdal.org/ogr/drv_shapefile.html) - m_strOGRVectorOutputExtension = ".shp"; - } - - else if (m_strVectorGISOutFormat == "geojson") - { - m_strOGRVectorOutputExtension = ".geojson"; - } - - else if (m_strVectorGISOutFormat == "gpkg") - { - m_strOGRVectorOutputExtension = ".gpkg"; - } - - // TODO 033 Others - - return true; -} - -//=============================================================================================================================== -//! The bSaveAllRasterGISFiles member function saves the raster GIS files using values from the RasterGrid array -//=============================================================================================================================== -bool CSimulation::bSaveAllRasterGISFiles(void) -{ - // Increment file number - m_nGISSave++; - - // Set for next save - if (m_bSaveRegular) - { - m_dRegularSaveTime += m_dRegularSaveInterval; - } - else - { - if (m_nThisSave < m_nUSave - 1) - { - // Still have user-defined save times remaining - m_nThisSave++; - } - else - { - // Finished user-defined times, switch to regular interval using last value as interval - double dLastInterval; - - if (m_nUSave > 1) - dLastInterval = m_dUSaveTime[m_nUSave - 1] - m_dUSaveTime[m_nUSave - 2]; - else - dLastInterval = m_dUSaveTime[m_nUSave - 1]; - - m_dRegularSaveTime = m_dSimElapsed + dLastInterval; - m_dRegularSaveInterval = dLastInterval; - m_bSaveRegular = true; - } - } - - if (m_bSedIncTalusTopSurfSave) - if (! bWriteRasterGISFile(RASTER_PLOT_SED_TOP_INC_TALUS_ELEV, &RASTER_PLOT_SED_TOP_INC_TALUS_ELEV_TITLE)) - return false; - - if (m_bTopSurfIncSeaSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOP_ELEV_INC_SEA, &RASTER_PLOT_TOP_ELEV_INC_SEA_TITLE)) - return false; - - if (m_bTalusSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TALUS, &RASTER_PLOT_TALUS_TITLE)) - return false; - - if (m_bSlopeConsSedSave) - if (! bWriteRasterGISFile(RASTER_PLOT_CONS_SED_SLOPE, &RASTER_PLOT_CONS_SED_SLOPE_TITLE)) - return false; - - if (m_bSlopeSaveForCliffToe) - if (! bWriteRasterGISFile(RASTER_PLOT_SLOPE_FOR_CLIFF_TOE, &RASTER_PLOT_SLOPE_FOR_CLIFF_TOE_TITLE)) - return false; - - if (m_bCliffToeSave) - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_TOE, &RASTER_PLOT_CLIFF_TOE_TITLE)) - return false; - - if (m_bSeaDepthSave) - if (! bWriteRasterGISFile(RASTER_PLOT_SEA_DEPTH, &RASTER_PLOT_SEA_DEPTH_TITLE)) - return false; - - if (m_bWaveHeightSave) - if (! bWriteRasterGISFile(RASTER_PLOT_WAVE_HEIGHT, &RASTER_PLOT_WAVE_HEIGHT_TITLE)) - return false; - - if (m_bWaveAngleSave) - if (! bWriteRasterGISFile(RASTER_PLOT_WAVE_ORIENTATION, &RASTER_PLOT_WAVE_ORIENTATION_TITLE)) - return false; - - // Don't write platform erosion files if there is no platform erosion - if (m_bDoShorePlatformErosion) - { - if (m_bPotentialPlatformErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_PLATFORM_EROSION, &RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_TITLE)) - return false; - - if (m_bActualPlatformErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_ACTUAL_PLATFORM_EROSION, &RASTER_PLOT_ACTUAL_PLATFORM_EROSION_TITLE)) - return false; - - if (m_bTotalPotentialPlatformErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION, &RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION_TITLE)) - return false; - - if (m_bTotalActualPlatformErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION, &RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION_TITLE)) - return false; - - if (m_bPotentialPlatformErosionMaskSave) - if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK, &RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK_TITLE)) - return false; - - if (m_bBeachProtectionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_PROTECTION, &RASTER_PLOT_BEACH_PROTECTION_TITLE)) - return false; - - if (m_bPotentialBeachErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_BEACH_EROSION, &RASTER_PLOT_POTENTIAL_BEACH_EROSION_TITLE)) - return false; - - if (m_bActualBeachErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_ACTUAL_BEACH_EROSION, &RASTER_PLOT_ACTUAL_BEACH_EROSION_TITLE)) - return false; - - if (m_bTotalPotentialBeachErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION, &RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION_TITLE)) - return false; - - if (m_bTotalActualBeachErosionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION, &RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION_TITLE)) - return false; - - if (m_bBeachDepositionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_DEPOSITION, &RASTER_PLOT_BEACH_DEPOSITION_TITLE)) - return false; - - if (m_bTotalBeachDepositionSave) - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_BEACH_DEPOSITION, &RASTER_PLOT_TOTAL_BEACH_DEPOSITION_TITLE)) - return false; - } - - if (m_bLandformSave) - if (! bWriteRasterGISFile(RASTER_PLOT_LANDFORM, &RASTER_PLOT_LANDFORM_TITLE)) - return false; - - if (m_bAvgWaveHeightSave) - if (! bWriteRasterGISFile(RASTER_PLOT_AVG_WAVE_HEIGHT, &RASTER_PLOT_AVG_WAVE_HEIGHT_TITLE)) - return false; - - if (m_bAvgWaveAngleSave) - if (! bWriteRasterGISFile(RASTER_PLOT_AVG_WAVE_ORIENTATION, &RASTER_PLOT_AVG_WAVE_ORIENTATION_TITLE)) - return false; - - if (m_bAvgSeaDepthSave) - if (! bWriteRasterGISFile(RASTER_PLOT_AVG_SEA_DEPTH, &RASTER_PLOT_AVG_SEA_DEPTH_TITLE)) - return false; - - if (m_bSedimentInput && m_bSedimentInputEventSave) - if (! bWriteRasterGISFile(RASTER_PLOT_SEDIMENT_INPUT, &RASTER_PLOT_SEDIMENT_INPUT_EVENT_TITLE)) - return false; - - // Don't write suspended sediment files if there is no fine sediment - if (m_bHaveFineSediment) - { - if (m_bSuspSedSave) - if (! bWriteRasterGISFile(RASTER_PLOT_SUSPENDED_SEDIMENT, &RASTER_PLOT_SUSPENDED_SEDIMENT_TITLE)) - return false; - - if (m_bAvgSuspSedSave) - if (! bWriteRasterGISFile(RASTER_PLOT_AVG_SUSPENDED_SEDIMENT, &RASTER_PLOT_AVG_SUSPENDED_SEDIMENT_TITLE)) - return false; - } - - if (m_bBasementElevSave) - if (! bWriteRasterGISFile(RASTER_PLOT_BASEMENT_ELEVATION, &RASTER_PLOT_BASEMENT_ELEVATION_TITLE)) - return false; - - for (int nLayer = 0; nLayer < m_nLayers; nLayer++) - { - if (m_bHaveFineSediment && m_bFineUnconsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - - if (m_bHaveSandSediment && m_bSandUnconsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - - if (m_bHaveCoarseSediment && m_bCoarseUnconsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - - if (m_bHaveFineSediment && m_bFineConsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - - if (m_bHaveSandSediment && m_bSandConsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - - if (m_bHaveCoarseSediment && m_bCoarseConsSedSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) - return false; - } - } - - if (m_bSliceSave) - { - for (int i = 0; i < static_cast(m_VdSliceElev.size()); i++) - { - if (! bWriteRasterGISFile(RASTER_PLOT_SLICE, &RASTER_PLOT_SLICE_TITLE, 0, m_VdSliceElev[i])) - return false; - } - } - - if (m_bRasterCoastlineSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_COAST, &RASTER_PLOT_COAST_TITLE)) - return false; - } - - if (m_bRasterNormalProfileSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_NORMAL_PROFILE, &RASTER_PLOT_NORMAL_PROFILE_TITLE)) - return false; - } - - if (m_bActiveZoneSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_ACTIVE_ZONE, &RASTER_PLOT_ACTIVE_ZONE_TITLE)) - return false; - } - - // Don't write cliff collapse files if we aren't considering cliff collapse - if (m_bDoCliffCollapse) - { - if (m_bCliffCollapseSave) - { - if (m_bHaveFineSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE_TITLE)) - return false; - } - - if (m_bHaveSandSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND_TITLE)) - return false; - } - - if (m_bHaveCoarseSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE_TITLE)) - return false; - } - - if (m_bCliffNotchAllSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_NOTCH_ALL, &RASTER_PLOT_CLIFF_NOTCH_ALL_TITLE)) - return false; - } - -#ifdef _DEBUG - if (m_bCliffCollapseTimestepSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP, &RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP_TITLE)) - return false; - } -#endif - } - - if (m_bTotCliffCollapseSave) - { - if (m_bHaveFineSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_TITLE)) - return false; - } - - if (m_bHaveSandSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_TITLE)) - return false; - } - - if (m_bHaveCoarseSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_TITLE)) - return false; - } - } - - if (m_bCliffCollapseDepositionSave) - { - if (m_bHaveSandSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND, &RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE)) - return false; - } - - if (m_bHaveCoarseSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE, &RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE)) - return false; - } - } - - if (m_bTotCliffCollapseDepositionSave) - { - if (m_bHaveSandSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE)) - return false; - } - - if (m_bHaveCoarseSediment) - { - if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE)) - return false; - } - } - } - - if (m_bRasterPolygonSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON, &RASTER_PLOT_POLYGON_TITLE)) - return false; - } - - if (m_bSeaMaskSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_INUNDATION_MASK, &RASTER_PLOT_INUNDATION_MASK_TITLE)) - return false; - } - - if (m_bBeachMaskSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_MASK, &RASTER_PLOT_BEACH_MASK_TITLE)) - return false; - } - - if (m_bInterventionClassSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_INTERVENTION_CLASS, &RASTER_PLOT_INTERVENTION_CLASS_TITLE)) - return false; - } - - if (m_bInterventionHeightSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_INTERVENTION_HEIGHT, &RASTER_PLOT_INTERVENTION_HEIGHT_TITLE)) - return false; - } - - if (m_bShadowZoneCodesSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_SHADOW_ZONE, &RASTER_PLOT_SHADOW_ZONE_TITLE)) - return false; - - if (! bWriteRasterGISFile(RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE, &RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE_TITLE)) - return false; - } - - if (m_bDeepWaterWaveAngleSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION, &RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION_TITLE)) - return false; - } - - if (m_bDeepWaterWaveHeightSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT, &RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT_TITLE)) - return false; - } - - if (m_bDeepWaterWavePeriodSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_PERIOD, &RASTER_PLOT_DEEP_WATER_WAVE_PERIOD_TITLE)) - return false; - } - - if (m_bPolygonUnconsSedUpOrDownDriftSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT, &RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT_TITLE)) - return false; - } - - if (m_bPolygonUnconsSedGainOrLossSave) - { - if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON_GAIN_OR_LOSS, &RASTER_PLOT_POLYGON_GAIN_OR_LOSS_TITLE)) - return false; - } - - // if (m_bSetupSurgeFloodMaskSave) - // { - // if (! bWriteRasterGISFile(RASTER_PLOT_SETUP_SURGE_FLOOD_MASK, &RASTER_PLOT_SETUP_SURGE_FLOOD_MASK_TITLE)) - // return false; - // } - - // if (m_bSetupSurgeRunupFloodMaskSave) - // { - // if (! bWriteRasterGISFile(RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK, &RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK_TITLE)) - // return false; - // } - // - return true; -} - -//=============================================================================================================================== -//! The bSaveAllvectorGISFiles member function saves the vector GIS files TODO 081 Choose more files to omit from "usual" vector output -//=============================================================================================================================== -bool CSimulation::bSaveAllVectorGISFiles(void) -{ - // Always written - if (m_bCoastSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_COAST, &VECTOR_PLOT_COAST_TITLE)) - return false; - - if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_SWL_HIGHEST, &VECTOR_PLOT_COAST_SWL_HIGHEST_TITLE)) - return false; - - if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_SWL_LOWEST, &VECTOR_PLOT_COAST_SWL_LOWEST_TITLE)) - return false; - } - - if (m_bCliffEdgeSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_CLIFF_EDGE, &VECTOR_PLOT_CLIFF_EDGE_TITLE)) - return false; - } - - if (m_bNormalsSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_NORMALS, &VECTOR_PLOT_NORMALS_TITLE)) - return false; - } - - if (m_bInvalidNormalsSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_INVALID_NORMALS, &VECTOR_PLOT_INVALID_NORMALS_TITLE)) - return false; - } - - if (m_bCoastCurvatureSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_CURVATURE, &VECTOR_PLOT_COAST_CURVATURE_TITLE)) - return false; - } - - if (m_bWaveAngleAndHeightSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT_TITLE)) - return false; - } - - if (m_bAvgWaveAngleAndHeightSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT_TITLE)) - return false; - } - - if (m_bWaveEnergySinceCollapseSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE, &VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE_TITLE)) - return false; - } - - if (m_bMeanWaveEnergySave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_MEAN_WAVE_ENERGY, &VECTOR_PLOT_MEAN_WAVE_ENERGY_TITLE)) - return false; - } - - if (m_bBreakingWaveHeightSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_BREAKING_WAVE_HEIGHT, &VECTOR_PLOT_BREAKING_WAVE_HEIGHT_TITLE)) - return false; - } - - if (m_bPolygonNodeSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_NODES, &VECTOR_PLOT_POLYGON_NODES_TITLE)) - return false; - } - - if (m_bPolygonBoundarySave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_BOUNDARY, &VECTOR_PLOT_POLYGON_BOUNDARY_TITLE)) - return false; - } - - if (m_bCliffNotchSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_CLIFF_NOTCH_ACTIVE, &VECTOR_PLOT_CLIFF_NOTCH_ACTIVE_TITLE)) - return false; - } - - if (m_bWaveTransectPointsSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_TRANSECT_POINTS, &VECTOR_PLOT_WAVE_TRANSECT_POINTS_TITLE)) - return false; - } - - if (m_bShadowBoundarySave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_SHADOW_ZONE_BOUNDARY, &VECTOR_PLOT_SHADOW_ZONE_BOUNDARY_TITLE)) - return false; - } - - if (m_bShadowDowndriftBoundarySave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY, &VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY_TITLE)) - return false; - } - - if (m_bDeepWaterWaveAngleAndHeightSave) - { - if (! bWriteVectorGISFile(VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_TITLE)) - return false; - } - - // if (m_bWaveSetupSave) - // { - // if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_SETUP, &VECTOR_PLOT_WAVE_SETUP_TITLE)) - // return false; - // } - // - // if (m_bStormSurgeSave) - // { - // if (! bWriteVectorGISFile(VECTOR_PLOT_STORM_SURGE, &VECTOR_PLOT_STORM_SURGE_TITLE)) - // return false; - // } - // - // if (m_bRunUpSave) - // { - // if (! bWriteVectorGISFile(VECTOR_PLOT_RUN_UP, &VECTOR_PLOT_RUN_UP_TITLE)) - // return false; - // } - // - // if (m_bRiverineFlooding && m_bVectorWaveFloodLineSave) - // { - // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_LINE, &VECTOR_PLOT_FLOOD_SWL_SETUP_LINE_TITLE)) - // return false; - // - // // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE, - // // &VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE_TITLE)) return false; - // - // // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE, - // // &VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_TITLE)) return false; - // } - - return true; -} - -//=============================================================================================================================== -//! Finds the max and min values in order to scale raster output if we cannot write doubles -//=============================================================================================================================== -void CSimulation::GetRasterOutputMinMax(int const nDataItem, double& dMin, double& dMax, int const nLayer, double const dElev) -{ - // If this is a binary mask layer, we already know the max and min values - if ((nDataItem == RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK) || - (nDataItem == RASTER_PLOT_INUNDATION_MASK) || - (nDataItem == RASTER_PLOT_BEACH_MASK) || - (nDataItem == RASTER_PLOT_COAST) || - (nDataItem == RASTER_PLOT_NORMAL_PROFILE) || - (nDataItem == RASTER_PLOT_ACTIVE_ZONE) || - (nDataItem == RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT) || - (nDataItem == RASTER_PLOT_SETUP_SURGE_FLOOD_MASK) || - (nDataItem == RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK) || - (nDataItem == RASTER_PLOT_WAVE_FLOOD_LINE)) - { - dMin = 0; - dMax = 1; - - return; - } - - // Not a binary mask layer, so we must find the max and min values - dMin = DBL_MAX; - dMax = DBL_MIN; - - double dTmp = 0; - - for (int nY = 0; nY < m_nYGridSize; nY++) - { - for (int nX = 0; nX < m_nXGridSize; nX++) - { - switch (nDataItem) - { - case (RASTER_PLOT_SLICE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetLayerAtElev(dElev); - break; - - case (RASTER_PLOT_LANDFORM): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); - break; - - case (RASTER_PLOT_INTERVENTION_CLASS): - dTmp = INT_NODATA; - - if (bIsInterventionCell(nX, nY)) - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); - - break; - - case (RASTER_PLOT_INTERVENTION_HEIGHT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetInterventionHeight(); - break; - - case (RASTER_PLOT_POLYGON): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - break; - - case (RASTER_PLOT_BASEMENT_ELEVATION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBasementElev(); - break; - - case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus(); - break; - - case (RASTER_PLOT_TALUS): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTalusDepth(); - break; - - case (RASTER_PLOT_TOP_ELEV_INC_SEA): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTopElevIncSea(); - break; - - case (RASTER_PLOT_CONS_SED_SLOPE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetConsSedSlope(); - break; - - case (RASTER_PLOT_SEA_DEPTH): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSeaDepth(); - break; - - case (RASTER_PLOT_AVG_SEA_DEPTH): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSeaDepth() / static_cast(m_ulIter); - break; - - case (RASTER_PLOT_WAVE_HEIGHT): - if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) - dTmp = m_dMissingValue; - else - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); - - break; - - case (RASTER_PLOT_AVG_WAVE_HEIGHT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveHeight() / static_cast(m_ulIter); - break; - - case (RASTER_PLOT_WAVE_ORIENTATION): - if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) - dTmp = m_dMissingValue; - - else - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); - - break; - - case (RASTER_PLOT_AVG_WAVE_ORIENTATION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveAngle() / static_cast(m_ulIter); - break; - - case (RASTER_PLOT_BEACH_PROTECTION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachProtectionFactor(); - - if (! bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) - dTmp = 1 - dTmp; // Output the inverse, seems more intuitive - - break; - - case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialPlatformErosion(); - break; - - case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualPlatformErosion(); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialPlatformErosion(); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualPlatformErosion(); - break; - - case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialBeachErosion(); - break; - - case (RASTER_PLOT_ACTUAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualBeachErosion(); - break; - - case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialBeachErosion(); - break; - - case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualBeachErosion(); - break; - - case (RASTER_PLOT_BEACH_DEPOSITION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachDeposition(); - break; - - case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotBeachDeposition(); - break; - - case (RASTER_PLOT_SUSPENDED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSuspendedSediment(); - break; - - case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSuspendedSediment() / - static_cast(m_ulIter); - break; - - case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetFineDepth(); - break; - - case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetSandDepth(); - break; - - case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetCoarseDepth(); - break; - - case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetFineDepth(); - break; - - case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetSandDepth(); - break; - - case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetCoarseDepth(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionFine(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionSand(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionCoarse(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseFine(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseSand(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseCoarse(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseSandTalusDeposition(); - break; - - case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseCoarseTalusDeposition(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSandTalusDeposition(); - break; - - case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCoarseTalusDeposition(); - break; - - case (RASTER_PLOT_SHADOW_ZONE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber(); - break; - - case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); - break; - - case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): - dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); - break; - - case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): - int const nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - int const nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); - - if (nPoly == INT_NODATA) - dTmp = m_dMissingValue; - else - dTmp = m_VCoast[nPolyCoast].pGetPolygon(nPoly)->dGetBeachDepositionAndSuspensionAllUncons(); - - break; - } - - if (! bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) - { - if (dTmp > dMax) - dMax = dTmp; - - if (dTmp < dMin) - dMin = dTmp; - } - } - } -} - -//=============================================================================================================================== -//! Sets per-driver defaults for raster files created using GDAL -//=============================================================================================================================== -void CSimulation::SetRasterFileCreationDefaults(void) -{ - string const strDriver = strToLower(&m_strRasterGISOutFormat); - string const strComment = "Created by " + PROGRAM_NAME + " for " + PLATFORM + " " + strGetBuild() + " running on " + strGetComputerName(); - - // TODO 034 Do these for all commonly-used file types - if (strDriver == "aaigrid") - { - } - else if (strDriver == "bmp") - { - } - else if (strDriver == "gtiff") - { - if (m_bWorldFile) - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "TFW", "YES"); - - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NUM_THREADS", "ALL_CPUS"); - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMPRESS", "LZW"); - } - else if (strDriver == "hfa") - { - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NBITS", "4"); - } - else if (strDriver == "jpeg") - { - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "QUALITY", "95"); - } - else if (strDriver == "png") - { - if (m_bWorldFile) - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "WORLDFILE", "YES"); - - // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "TITLE", "This is the title"); m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "DESCRIPTION", "This is a description"); - // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COPYRIGHT", "This is some copyright statement"); - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NBITS", "4"); - } - else if (strDriver == "rst") - { - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); - } - else if (strDriver == "geojson") - { - // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); - } - else if (strDriver == "gpkg") - { - // TODO 065 Does GDAL support overwriting raster gpkg files yet? - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "OVERWRITE", "YES"); - m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "USE_TILE_EXTENT", "YES"); - } - else if (strDriver == "netcdf") - { - } -} - -//=============================================================================================================================== -//! Returns the opposite direction -//=============================================================================================================================== -int CSimulation::nGetOppositeDirection(int const nDirection) -{ - switch (nDirection) - { - case NORTH: - return SOUTH; - - case NORTH_EAST: - return SOUTH - WEST; - - case EAST: - return WEST; - - case SOUTH_EAST: - return NORTH - WEST; - - case SOUTH: - return NORTH; - - case SOUTH_WEST: - return NORTH - EAST; - - case WEST: - return EAST; - - case NORTH_WEST: - return SOUTH - EAST; - } - - // Should never get here - return NO_DIRECTION; -} - -// //=============================================================================================================================== -// //! Given two integer points, calculates the slope and intercept of the line passing through the points -// //=============================================================================================================================== -// void CSimulation::GetSlopeAndInterceptFromPoints(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2, double& dSlope, double& dIntercept) -// { -// int nX1 = pPti1->nGetX(); -// int nY1 = pPti1->nGetY(); -// int nX2 = pPti2->nGetX(); -// int nY2 = pPti2->nGetY(); -// -// double dXDiff = nX1 - nX2; -// double dYDiff = nY1 - nY2; -// -// if (bFPIsEqual(dXDiff, 0.0, TOLERANCE)) -// dSlope = 0; -// else -// dSlope = dYDiff / dXDiff; -// -// dIntercept = nY1 - (dSlope * nX1); -// } - -//=============================================================================================================================== -//! Finds the closest point on any coastline to a given point -//=============================================================================================================================== -CGeom2DIPoint CSimulation::PtiFindClosestCoastPoint(int const nX, int const nY, int& nCoastFound) -{ - unsigned int nMinSqDist = UINT_MAX; - CGeom2DIPoint PtiCoastPoint; - - // Do for every coast - for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) - { - for (int j = 0; j < m_VCoast[nCoast].nGetCoastlineSize(); j++) - { - // Get the coords of the grid cell marked as coastline for the coastal landform object - int const nXCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetX(); - int const nYCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetY(); - - // Calculate the squared distance between this point and the given point - int const nXDist = nX - nXCoast; - int const nYDist = nY - nYCoast; - - unsigned int const nSqDist = (nXDist * nXDist) + (nYDist * nYDist); - - // Is this the closest so dar? - if (nSqDist < nMinSqDist) - { - nMinSqDist = nSqDist; - PtiCoastPoint.SetXY(nXCoast, nYCoast); - nCoastFound = nCoast; - } - } - } - - return PtiCoastPoint; -} - -//=============================================================================================================================== -//! Finds the number of the closest point on any coastline to a given point, or INT_NODATA in case of error -//=============================================================================================================================== -int CSimulation::nFindClosestCoastPoint(int const nX, int const nY, int& nCoastFound) -{ - unsigned int nMinSqDist = UINT_MAX; - int nCoastPoint = INT_NODATA; - - // Do for every coast - for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) - { - for (int j = 0; j < m_VCoast[nCoast].nGetCoastlineSize(); j++) - { - // Get the coords of the grid cell marked as coastline for the coastal landform object - int const nXCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetX(); - int const nYCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetY(); - - // Calculate the squared distance between this point and the given point - int const nXDist = nX - nXCoast; - int const nYDist = nY - nYCoast; - - unsigned int const nSqDist = (nXDist * nXDist) + (nYDist * nYDist); - - // Is this the closest so dar? - if (nSqDist < nMinSqDist) - { - nMinSqDist = nSqDist; - nCoastPoint = j; - nCoastFound = nCoast; - } - } - } - - return nCoastPoint; -} - -//=============================================================================================================================== -//! Given a length in m, this returns the rounded equivalent number of cells -//=============================================================================================================================== -int CSimulation::nConvertMetresToNumCells(double const dLen) const -{ - return nRound(dLen / m_dCellSide); -} - -//=============================================================================================================================== -//! For the straight line between two points A and B, and given another point C, this finds the closest point on the line A-B to the point C. From https://cboard.cprogramming.com/c-programming/155809-find-closest-point-line.html -//=============================================================================================================================== -void CSimulation::FindClosestPointOnStraightLine(double const dAx, double const dAy, double const dBx, double const dBy, double const dPx, double const dPy, double& dXRet, double& dYRet) -{ - double const dAPx = dPx - dAx; - double const dAPy = dPy - dAy; - double const dABx = dBx - dAx; - double const dABy = dBy - dAy; - double const dMagAB2 = (dABx * dABx) + (dABy * dABy); - double const dABdotAP = (dABx * dAPx) + (dABy * dAPy); - double const dT = dABdotAP / dMagAB2; - - if ( dT < 0) - { - dXRet = dAx; - dYRet = dAy; - } - else if (dT > 1) - { - dXRet = dBx; - dYRet = dBy; - } - else - { - dXRet = dAx + (dABx * dT); - dYRet = dAy + (dABy * dT); - } -} - -//=============================================================================================================================== -//! Given two different edge cells, returns true if they are adjacent -//=============================================================================================================================== -bool CSimulation::bIsAdjacentEdgeCell(CGeom2DIPoint const* pPt1, CGeom2DIPoint const* pPt2) -{ - int const nX1 = pPt1->nGetX(); - int const nY1 = pPt1->nGetY(); - int const nX2 = pPt2->nGetX(); - int const nY2 = pPt2->nGetY(); - - if (nX1 == nX2) - { - if ((nY1 == nY2 + 1) || (nY1 == nY2 - 1)) - return true; - } - - if (nY1 == nY2) - { - if ((nX1 == nX2 + 1) || (nX1 == nX2 - 1)) - return true; - } - - return false; -} +/*! + \file gis_utils.cpp + \brief Various GIS-related functions, requires GDAL + \details Note re. coordinate systems used + + 1. In the raster CRS, cell[0][0] is at the top left (NW) corner of the grid. Raster grid co-oordinate [0][0] is actually the top left (NW) corner of this cell. + + 2. We assume that the grid CRS and external CRS have parallel axes. If they have not, see http://www.gdal.org/classGDALDataset.html which says that: + + To convert between pixel/line (P,L) raster space, and projection coordinates (Xp,Yp) space Xp = padfTransform[0] + padfTransform[1] + padfTransform[2]; Yp + = padfTransform[3] + padfTransform[4] + padfTransform[5]; + + In a north-up image, padfTransform[1] is the pixel width, and padfTransform[5] is the pixel height. The upper left corner of the upper left pixel is at position (padfTransform[0], padfTransform[3]). + + 3. Usually, raster grid CRS values are integer, i.e. they refer to a point which is at the centroid of a cell. They may also be -ve or greater than m_nXGridSize-1 i.e. may refer to a point which lies outside any cell of the raster grid. + + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include + +#include + +#include +using std::vector; + +#include +using std::cerr; +using std::endl; +using std::ios; + +#include +using std::atan2; + +#include +#include +#include + +#include +using std::strstr; + +#include +#include +#include + +#include "cme.h" +#include "coast.h" +#include "raster_grid.h" +#include "2d_point.h" +#include "2di_point.h" + +//=============================================================================================================================== +//! Given the integer X-axis ordinate of a cell in the raster grid CRS, returns the external CRS X-axis ordinate of the cell's centroid +//=============================================================================================================================== +double CSimulation::dGridCentroidXToExtCRSX(int const nGridX) const +{ + // TODO 064 + return (m_dGeoTransform[0] + (nGridX * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2)); +} + +//=============================================================================================================================== +//! Given the integer Y-axis ordinate of a cell in the raster grid CRS, returns the external CRS Y-axis ordinate of the cell's centroid +//=============================================================================================================================== +double CSimulation::dGridCentroidYToExtCRSY(int const nGridY) const +{ + // TODO 064 + return (m_dGeoTransform[3] + (nGridY * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2)); +} + +//=============================================================================================================================== +//! Transforms a pointer to a CGeom2DIPoint in the raster grid CRS (assumed to be the centroid of a cell) to the equivalent CGeom2DPoint in the external CRS +//=============================================================================================================================== +CGeom2DPoint CSimulation::PtGridCentroidToExt(CGeom2DIPoint const *pPtiIn) const +{ + // TODO 064 + double const dX = m_dGeoTransform[0] + (pPtiIn->nGetX() * m_dGeoTransform[1]) + (m_dGeoTransform[1] / 2); + double const dY = m_dGeoTransform[3] + (pPtiIn->nGetY() * m_dGeoTransform[5]) + (m_dGeoTransform[5] / 2); + + return CGeom2DPoint(dX, dY); +} + +//=============================================================================================================================== +//! Given a real-valued X-axis ordinate in the raster grid CRS (i.e. not the centroid of a cell), returns the external CRS X-axis ordinate +//=============================================================================================================================== +double CSimulation::dGridXToExtCRSX(double const dGridX) const +{ + // TODO 064 Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2) + return m_dGeoTransform[0] + (dGridX * m_dGeoTransform[1]) - 1; +} + +//=============================================================================================================================== +//! Given a real-valued Y-axis ordinate in the raster grid CRS (i.e. not the centroid of a cell), returns the external CRS Y-axis ordinate +//=============================================================================================================================== +double CSimulation::dGridYToExtCRSY(double const dGridY) const +{ + // TODO 064 Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5) + return m_dGeoTransform[3] + (dGridY * m_dGeoTransform[5]) - 1; +} + +//=============================================================================================================================== +//! Transforms an X-axis ordinate in the external CRS to the equivalent X-axis ordinate in the raster grid CRS (the result is not rounded, and so may not be integer, and may be outside the grid) +//=============================================================================================================================== +double CSimulation::dExtCRSXToGridX(double const dExtCRSX) const +{ + // TODO 064 + return ((dExtCRSX - m_dGeoTransform[0]) / m_dGeoTransform[1]) - 1; +} + +//=============================================================================================================================== +//! Transforms a Y-axis ordinate in the external CRS to the equivalent Y-axis ordinate in the raster grid CRS (the result is not rounded, and so may not be integer, and may be outside the grid) +//=============================================================================================================================== +double CSimulation::dExtCRSYToGridY(double const dExtCRSY) const +{ + // TODO 064 + return ((dExtCRSY - m_dGeoTransform[3]) / m_dGeoTransform[5]) - 1; +} + +//=============================================================================================================================== +//! Transforms a pointer to a CGeom2DPoint in the external CRS to the equivalent CGeom2DIPoint in the raster grid CRS (both values rounded). The result may be outside the grid +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiExtCRSToGridRound(CGeom2DPoint const* pPtIn) const +{ + // TODO 064 + int const nX = nRound(((pPtIn->dGetX() - m_dGeoTransform[0]) / m_dGeoTransform[1]) - 1); + int const nY = nRound(((pPtIn->dGetY() - m_dGeoTransform[3]) / m_dGeoTransform[5]) - 1); + + return CGeom2DIPoint(nX, nY); +} + +//=============================================================================================================================== +//! Returns the distance (in external CRS) between two points +//=============================================================================================================================== +double CSimulation::dGetDistanceBetween(CGeom2DPoint const* Pt1, CGeom2DPoint const* Pt2) +{ + double const dXDist = Pt1->dGetX() - Pt2->dGetX(); + double const dYDist = Pt1->dGetY() - Pt2->dGetY(); + + return hypot(dXDist, dYDist); +} + +//=============================================================================================================================== +//! Returns the distance (in external CRS) between two points +//=============================================================================================================================== +double CSimulation::dGetDistanceBetween(CGeom2DIPoint const* Pti1, CGeom2DIPoint const* Pti2) +{ + double const dXDist = Pti1->nGetX() - Pti2->nGetX(); + double const dYDist = Pti1->nGetY() - Pti2->nGetY(); + + return hypot(dXDist, dYDist); +} + +//=============================================================================================================================== +//! Returns the distance (in external CRS) between two points +//=============================================================================================================================== +double CSimulation::dGetDistanceBetween(double const dX1, double const dY1, double const dX2, double const dY2) +{ + double const dXDist = dX1 - dX2; + double const dYDist = dY1 - dY2; + + return hypot(dXDist, dYDist); +} + +//=============================================================================================================================== +//! Returns twice the signed area of a triangle, defined by three points +//=============================================================================================================================== +double CSimulation::dTriangleAreax2(CGeom2DPoint const* pPtA, CGeom2DPoint const* pPtB, CGeom2DPoint const* pPtC) +{ + return (pPtB->dGetX() - pPtA->dGetX()) * (pPtC->dGetY() - pPtA->dGetY()) - (pPtB->dGetY() - pPtA->dGetY()) * (pPtC->dGetX() - pPtA->dGetX()); +} + +//=============================================================================================================================== +//! Checks whether the supplied point (an x-y pair, in the grid CRS) is within the raster grid, and is a valid cell (i.e. the basement DEM is not NODATA) +//=============================================================================================================================== +bool CSimulation::bIsWithinValidGrid(int const nX, int const nY) const +{ + if ((nX < 0) || (nX >= m_nXGridSize)) + return false; + + if ((nY < 0) || (nY >= m_nYGridSize)) + return false; + + if (m_pRasterGrid->m_Cell[nX][nY].bBasementElevIsMissingValue()) + return false; + + return true; +} + +//=============================================================================================================================== +//! Checks whether the supplied point (a reference to a CGeom2DIPoint, in the grid CRS) is within the raster grid, and is a valid cell (i.e. the basement DEM is not NODATA) +//=============================================================================================================================== +bool CSimulation::bIsWithinValidGrid(CGeom2DIPoint const* Pti) const +{ + int const nX = Pti->nGetX(); + int const nY = Pti->nGetY(); + + return this->bIsWithinValidGrid(nX, nY); +} + +//=============================================================================================================================== +//! Constrains the supplied point (in the grid CRS) to be a valid cell within the raster grid +//=============================================================================================================================== +void CSimulation::KeepWithinValidGrid(int& nX, int& nY) const +{ + nX = tMax(nX, 0); + nX = tMin(nX, m_nXGridSize - 1); + + nY = tMax(nY, 0); + nY = tMin(nY, m_nYGridSize - 1); +} + +//=============================================================================================================================== +//! Constrains the second supplied point (both are CGeom2DIPoints, in the grid CRS) to be a valid cell within the raster grid +//=============================================================================================================================== +void CSimulation::KeepWithinValidGrid(CGeom2DIPoint const* Pti0, CGeom2DIPoint* Pti1) const +{ + KeepWithinValidGrid(Pti0->nGetX(), Pti0->nGetY(), *Pti1->pnGetX(), *Pti1->pnGetY()); +} + +//=============================================================================================================================== +//! Given two points in the grid CRS (the points assumed not to be coincident), this routine modifies the value of the second point so that it is on a line joining the original two points and is a valid cell within the raster grid. However in some cases (e.g. if the first point is at the edge of the valid part of the raster grid) then the second cell will be coincident with the +//! first cell, and the line joining them is thus of zero length. The calling routine has to be able to handle this +//=============================================================================================================================== +void CSimulation::KeepWithinValidGrid(int nX0, int nY0, int& nX1, int& nY1) const +{ + // Safety check: make sure that the first point is within the valid grid + if (nX0 >= m_nXGridSize) + nX0 = m_nXGridSize - 1; + + else if (nX0 < 0) + nX0 = 0; + + if (nY0 >= m_nYGridSize) + nY0 = m_nYGridSize - 1; + + else if (nY0 < 0) + nY0 = 0; + + // OK let's go + int const nDiffX = nX0 - nX1; + int const nDiffY = nY0 - nY1; + + if (nDiffX == 0) + { + // The two points have the same x coordinates, so we just need to constrain the y co-ord + if (nY1 < nY0) + { + nY1 = -1; + + do + { + nY1++; + } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); + + return; + } + else + { + nY1 = m_nYGridSize; + + do + { + nY1--; + } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); + + return; + } + } + else if (nDiffY == 0) + { + // The two points have the same y coordinates, so we just need to constrain the x co-ord + if (nX1 < nX0) + { + nX1 = -1; + + do + { + nX1++; + } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); + + return; + } + else + { + nX1 = m_nXGridSize; + + do + { + nX1--; + } while (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue()); + + return; + } + } + else + { + // The two points have different x coordinates and different y coordinates, so we have to work harder. First find which of the coordinates is the greatest distance outside the grid, and constrain that co-ord for efficiency (since this will reduce the number of times round the loop). Note that both may be inside the grid, if the incorrect co-ord is in the invalid margin, in which case arbitrarily contrain the x co-ord + int nXDistanceOutside = 0; + int nYDistanceOutside = 0; + + if (nX1 < 0) + nXDistanceOutside = -nX1; + else if (nX1 >= m_nXGridSize) + nXDistanceOutside = nX1 - m_nXGridSize + 1; + + if (nY1 < 0) + nYDistanceOutside = -nY1; + else if (nY1 >= m_nYGridSize) + nXDistanceOutside = nY1 - m_nYGridSize + 1; + + if (nXDistanceOutside >= nYDistanceOutside) + { + // Constrain the x co-ord + if (nX1 < nX0) + { + // The incorrect x co-ord is less than the correct x co-ord: constrain it and find the y co-ord + nX1 = -1; + + do + { + nX1++; + + nY1 = nY0 + nRound(((nX1 - nX0) * nDiffY) / static_cast(nDiffX)); + } while ((nY1 < 0) || (nY1 >= m_nYGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); + + return; + } + else + { + // The incorrect x co-ord is greater than the correct x-co-ord: constrain it and find the y co-ord + nX1 = m_nXGridSize; + + do + { + nX1--; + + nY1 = nY0 + nRound(((nX1 - nX0) * nDiffY) / static_cast(nDiffX)); + } while ((nY1 < 0) || (nY1 >= m_nYGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); + + return; + } + } + else + { + // Constrain the y co-ord + if (nY1 < nY0) + { + // The incorrect y co-ord is less than the correct y-co-ord: constrain it and find the x co-ord + nY1 = -1; + + do + { + nY1++; + + nX1 = nX0 + nRound(((nY1 - nY0) * nDiffX) / static_cast(nDiffY)); + } while ((nX1 < 0) || (nX1 >= m_nXGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); + + return; + } + else + { + // The incorrect y co-ord is greater than the correct y co-ord: constrain it and find the x co-ord + nY1 = m_nYGridSize; + + do + { + nY1--; + + nX1 = nX0 + + nRound(((nY1 - nY0) * nDiffX) / static_cast(nDiffY)); + } while ((nX1 < 0) || (nX1 >= m_nXGridSize) || (m_pRasterGrid->m_Cell[nX1][nY1].bBasementElevIsMissingValue())); + + return; + } + } + } +} + +//=============================================================================================================================== +//! Constrains the supplied angle to be within 0 and 360 degrees +//=============================================================================================================================== +double CSimulation::dKeepWithin360(double const dAngle) +{ + double dNewAngle = dAngle; + + // Sort out -ve angles + while (dNewAngle < 0) + dNewAngle += 360; + + // Sort out angles > 360 + while (dNewAngle > 360) + dNewAngle -= 360; + + return dNewAngle; +} + +//=============================================================================================================================== +//! Returns a point (external CRS) which is the average of (i.e. is midway between) two other external CRS points +//=============================================================================================================================== +CGeom2DPoint CSimulation::PtAverage(CGeom2DPoint const* pPt1, CGeom2DPoint const* pPt2) +{ + double const dPt1X = pPt1->dGetX(); + double const dPt1Y = pPt1->dGetY(); + double const dPt2X = pPt2->dGetX(); + double const dPt2Y = pPt2->dGetY(); + double const dPtAvgX = (dPt1X + dPt2X) / 2; + double const dPtAvgY = (dPt1Y + dPt2Y) / 2; + + return CGeom2DPoint(dPtAvgX, dPtAvgY); +} + +// //=============================================================================================================================== +// //! Returns an integer point (grid CRS) which is the approximate average of (i.e. is midway between) two other grid CRS integer points +// //=============================================================================================================================== +// CGeom2DIPoint CSimulation::PtiAverage(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2) +// { +// int const nPti1X = pPti1->nGetX(); +// int const nPti1Y = pPti1->nGetY(); +// int const nPti2X = pPti2->nGetX(); +// int const nPti2Y = pPti2->nGetY(); +// int const nPtiAvgX = (nPti1X + nPti2X) / 2; +// int const nPtiAvgY = (nPti1Y + nPti2Y) / 2; +// +// return CGeom2DIPoint(nPtiAvgX, nPtiAvgY); +// } + +//=============================================================================================================================== +//! Returns an integer point (grid CRS) which is the weighted average of two other grid CRS integer points. The weight must be <= 1, if the weight is < 0.5 then the output point is closer to the first point, if the weight is > 0.5 then the output point is closer to the second point +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiWeightedAverage(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2, double const dWeight) +{ + int const nPti1X = pPti1->nGetX(); + int const nPti1Y = pPti1->nGetY(); + int const nPti2X = pPti2->nGetX(); + int const nPti2Y = pPti2->nGetY(); + double const dOtherWeight = 1.0 - dWeight; + + int const nPtiWeightAvgX = nRound((dWeight * nPti2X) + (dOtherWeight * nPti1X)); + int const nPtiWeightAvgY = nRound((dWeight * nPti2Y) + (dOtherWeight * nPti1Y)); + + return CGeom2DIPoint(nPtiWeightAvgX, nPtiWeightAvgY); +} + +//=============================================================================================================================== +//! Returns a point (external CRS) which is the average of a vector of external CRS points +//=============================================================================================================================== +CGeom2DPoint CSimulation::PtAverage(vector* pVIn) +{ + int const nSize = static_cast(pVIn->size()); + + if (nSize == 0) + return CGeom2DPoint(DBL_NODATA, DBL_NODATA); + + double dAvgX = 0; + double dAvgY = 0; + + for (int n = 0; n < nSize; n++) + { + dAvgX += pVIn->at(n).dGetX(); + dAvgY += pVIn->at(n).dGetY(); + } + + dAvgX /= nSize; + dAvgY /= nSize; + + return CGeom2DPoint(dAvgX, dAvgY); +} + +// //=============================================================================================================================== +// //! Returns a point (grid CRS) which is the average of a vector of grid CRS points +// //=============================================================================================================================== +// CGeom2DIPoint CSimulation::PtiAverage(vector* pVIn) +// { +// int nSize = static_cast(pVIn->size()); +// if (nSize == 0) +// return CGeom2DIPoint(INT_NODATA, INT_NODATA); +// +// double dAvgX = 0; +// double dAvgY = 0; +// +// for (int n = 0; n < nSize; n++) +// { +// dAvgX += pVIn->at(n).nGetX(); +// dAvgY += pVIn->at(n).nGetY(); +// } +// +// dAvgX /= nSize; +// dAvgY /= nSize; +// +// return CGeom2DIPoint(nRound(dAvgX), nRound(dAvgY)); +// } + +//=============================================================================================================================== +//! Returns an integer point (grid CRS) which is the centroid of a polygon, given by a vector of grid CRS points. From https://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiPolygonCentroid(vector* pVIn) +{ + CGeom2DIPoint PtiCentroid(0, 0); + int const nSize = static_cast(pVIn->size()); + int nX0 = 0; // Current vertex X + int nY0 = 0; // Current vertex Y + int nX1 = 0; // Next vertex X + int nY1 = 0; // Next vertex Y + + double dA = 0; // Partial signed area + double dSignedArea = 0.0; + + // For all vertices except last + for (int i = 0; i < nSize - 1; ++i) + { + nX0 = pVIn->at(i).nGetX(); + nY0 = pVIn->at(i).nGetY(); + nX1 = pVIn->at(i + 1).nGetX(); + nY1 = pVIn->at(i + 1).nGetY(); + + dA = (nX0 * nY1) - (nX1 * nY0); + dSignedArea += dA; + PtiCentroid.AddXAddY((nX0 + nX1) * dA, (nY0 + nY1) * dA); + } + + // Do last vertex separately to avoid performing an expensive modulus operation in each iteration + nX0 = pVIn->at(nSize - 1).nGetX(); + nY0 = pVIn->at(nSize - 1).nGetY(); + nX1 = pVIn->at(0).nGetX(); + nY1 = pVIn->at(0).nGetY(); + + dA = (nX0 * nY1) - (nX1 * nY0); + dSignedArea += dA; + PtiCentroid.AddXAddY((nX0 + nX1) * dA, (nY0 + nY1) * dA); + + dSignedArea *= 0.5; + PtiCentroid.DivXDivY(6.0 * dSignedArea, 6.0 * dSignedArea); + + return PtiCentroid; +} + +//=============================================================================================================================== +// Returns a vector which is perpendicular to an existing vector +//=============================================================================================================================== +// vector CSimulation::VGetPerpendicular(CGeom2DPoint const* +// PtStart, CGeom2DPoint const* PtNext, double const dDesiredLength, int const +// nHandedness) +// { +// // Returns a two-point vector which passes through PtStart with a scaled +// length +// double dXLen = PtNext->dGetX() - PtStart->dGetX(); +// double dYLen = PtNext->dGetY() - PtStart->dGetY(); +// +// double dLength = hypot(dXLen, dYLen); +// double dScaleFactor = dDesiredLength / dLength; +// +// // The difference vector is (dXLen, dYLen), so the perpendicular +// difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) +// CGeom2DPoint EndPt; +// if (nHandedness == RIGHT_HANDED) +// { +// EndPt.SetX(PtStart->dGetX() + (dScaleFactor * dYLen)); +// EndPt.SetY(PtStart->dGetY() - (dScaleFactor * dXLen)); +// } +// else +// { +// EndPt.SetX(PtStart->dGetX() - (dScaleFactor * dYLen)); +// EndPt.SetY(PtStart->dGetY() + (dScaleFactor * dXLen)); +// } +// +// vector VNew; +// VNew.push_back(*PtStart); +// VNew.push_back(EndPt); +// return VNew; +// } + +// //=============================================================================================================================== +// //! Returns a CGeom2DPoint which is the 'other' point of a two-point vector passing through PtStart, and which is perpendicular to the two-point vector from PtStart to PtNext +// //=============================================================================================================================== +// CGeom2DPoint CSimulation::PtGetPerpendicular(CGeom2DPoint const* PtStart, CGeom2DPoint const* PtNext, double const dDesiredLength, int const nHandedness) +// { +// double const dXLen = PtNext->dGetX() - PtStart->dGetX(); +// double const dYLen = PtNext->dGetY() - PtStart->dGetY(); +// double dLength; +// +// if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) +// dLength = dYLen; +// else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) +// dLength = dXLen; +// else +// dLength = hypot(dXLen, dYLen); +// +// double const dScaleFactor = dDesiredLength / dLength; +// +// // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) +// CGeom2DPoint EndPt; +// +// if (nHandedness == RIGHT_HANDED) +// { +// EndPt.SetX(PtStart->dGetX() + (dScaleFactor * dYLen)); +// EndPt.SetY(PtStart->dGetY() - (dScaleFactor * dXLen)); +// } +// else +// { +// EndPt.SetX(PtStart->dGetX() - (dScaleFactor * dYLen)); +// EndPt.SetY(PtStart->dGetY() + (dScaleFactor * dXLen)); +// } +// +// return EndPt; +// } + +//=============================================================================================================================== +//! Returns a CGeom2DIPoint (grid CRS) which is the 'other' point of a two-point vector passing through PtiStart, and which is perpendicular to the two-point vector from PtiStart to PtiNext +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiGetPerpendicular(CGeom2DIPoint const* PtiStart, CGeom2DIPoint const* PtiNext, double const dDesiredLength, int const nHandedness) +{ + double const dXLen = PtiNext->nGetX() - PtiStart->nGetX(); + double const dYLen = PtiNext->nGetY() - PtiStart->nGetY(); + double dLength; + + if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) + dLength = dYLen; + + else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) + dLength = dXLen; + + else + dLength = hypot(dXLen, dYLen); + + double const dScaleFactor = dDesiredLength / dLength; + + // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) + CGeom2DIPoint EndPti; + + if (nHandedness == RIGHT_HANDED) + { + EndPti.SetX(PtiStart->nGetX() + nRound(dScaleFactor * dYLen)); + EndPti.SetY(PtiStart->nGetY() - nRound(dScaleFactor * dXLen)); + } + + else + { + EndPti.SetX(PtiStart->nGetX() - nRound(dScaleFactor * dYLen)); + EndPti.SetY(PtiStart->nGetY() + nRound(dScaleFactor * dXLen)); + } + + return EndPti; +} + +//=============================================================================================================================== +//! Returns a CGeom2DIPoint (grid CRS) which is the 'other' point of a two-point vector passing through [nStartX][nStartY], and which is perpendicular to the two-point vector from [nStartX][nStartY] to [nNextX][nNextY] +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiGetPerpendicular(int const nStartX, int const nStartY, int const nNextX, int const nNextY, double const dDesiredLength, int const nHandedness) +{ + double const dXLen = nNextX - nStartX; + double const dYLen = nNextY - nStartY; + double dLength; + + if (bFPIsEqual(dXLen, 0.0, TOLERANCE)) + dLength = dYLen; + + else if (bFPIsEqual(dYLen, 0.0, TOLERANCE)) + dLength = dXLen; + + else + dLength = hypot(dXLen, dYLen); + + double const dScaleFactor = dDesiredLength / dLength; + + // The difference vector is (dXLen, dYLen), so the perpendicular difference vector is (-dYLen, dXLen) or (dYLen, -dXLen) + CGeom2DIPoint EndPti; + + if (nHandedness == RIGHT_HANDED) + { + EndPti.SetX(nStartX + nRound(dScaleFactor * dYLen)); + EndPti.SetY(nStartY - nRound(dScaleFactor * dXLen)); + } + + else + { + EndPti.SetX(nStartX - nRound(dScaleFactor * dYLen)); + EndPti.SetY(nStartY + nRound(dScaleFactor * dXLen)); + } + + return EndPti; +} + +//=============================================================================================================================== +//! Returns the signed angle BAC (in radians) subtended between three CGeom2DIPoints B A C. From http://stackoverflow.com/questions/3057448/angle-between-3-vertices +//=============================================================================================================================== +double CSimulation::dAngleSubtended(CGeom2DIPoint const* pPtiA, CGeom2DIPoint const* pPtiB, CGeom2DIPoint const* pPtiC) +{ + double const dXDistBtoA = pPtiB->nGetX() - pPtiA->nGetX(); + double const dYDistBtoA = pPtiB->nGetY() - pPtiA->nGetY(); + double const dXDistCtoA = pPtiC->nGetX() - pPtiA->nGetX(); + double const dYDistCtoA = pPtiC->nGetY() - pPtiA->nGetY(); + double const dDotProduct = dXDistBtoA * dXDistCtoA + dYDistBtoA * dYDistCtoA; + double const dPseudoCrossProduct = dXDistBtoA * dYDistCtoA - dYDistBtoA * dXDistCtoA; + double const dAngle = atan2(dPseudoCrossProduct, dDotProduct); + + return dAngle; +} + +//=============================================================================================================================== +//! Checks whether the selected raster GDAL driver supports file creation, 32-bit doubles, etc. +//=============================================================================================================================== +bool CSimulation::bCheckRasterGISOutputFormat(void) +{ + // Register all available GDAL raster and vector drivers (GDAL 2) + GDALAllRegister(); + + // If the user hasn't specified a GIS output format, assume that we will use the same GIS format as the input basement DEM + if (m_strRasterGISOutFormat.empty()) + m_strRasterGISOutFormat = m_strGDALBasementDEMDriverCode; + + // Load the raster GDAL driver + GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName(m_strRasterGISOutFormat.c_str()); + + if (NULL == pDriver) + { + // Can't load raster GDAL driver. Incorrectly specified? + cerr << ERR << "Unknown raster GIS output format '" << m_strRasterGISOutFormat << "'." << endl; + return false; + } + + // Get the metadata for this raster driver + char **papszMetadata = pDriver->GetMetadata(); + + // for (int i = 0; papszMetadata[i] != NULL; i++) + // cout << papszMetadata[i] << endl; + // cout << endl; + + // Need to test if this is a raster driver + if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_RASTER, false)) + { + // This is not a raster driver + cerr << ERR << "GDAL driver '" << m_strRasterGISOutFormat << "' is not a raster driver. Choose another format." << endl; + return false; + } + + // This driver is OK, so store its longname and the default file extension + string strTmp = CSLFetchNameValue(papszMetadata, "DMD_LONGNAME"); + m_strGDALRasterOutputDriverLongname = strTrim(&strTmp); + strTmp = CSLFetchNameValue(papszMetadata, "DMD_EXTENSIONS"); // Note DMD_EXTENSION (no S, is a single value) appears not to be implemented for newer drivers + strTmp = strTrim(&strTmp); + + // We have a space-separated list of one or more file extensions: use the first extension in the list + long unsigned int const nPos = strTmp.find(SPACE); + + if (nPos == string::npos) + { + // No space i.e. just one extension + m_strGDALRasterOutputDriverExtension = strTmp; + } + + else + { + // There's a space, so we must have more than one extension + m_strGDALRasterOutputDriverExtension = strTmp.substr(0, nPos); + } + + // Set up any defaults for raster files that are created using this driver + SetRasterFileCreationDefaults(); + + // Now do various tests of the driver's capabilities + if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATE, false)) + { + // This raster driver does not support the Create() method, does it support CreateCopy()? + if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATECOPY, false)) + { + cerr << ERR << "Cannot write using raster GDAL driver '" << m_strRasterGISOutFormat << " since neither Create() or CreateCopy() are supported'. Choose another GDAL raster format." << endl; + return false; + } + + // Can't use Create() but can use CreateCopy() + m_bGDALCanCreate = false; + } + + // Next, test to see what data types the driver can write and from this, work out the largest int and float we can write + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Float")) + { + m_bGDALCanWriteFloat = true; + m_GDALWriteFloatDataType = GDT_Float32; + } + + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "UInt32")) + { + m_bGDALCanWriteInt32 = true; + + m_GDALWriteIntDataType = GDT_UInt32; + m_lGDALMaxCanWrite = UINT32_MAX; + m_lGDALMinCanWrite = 0; + + if (! m_bGDALCanWriteFloat) + m_GDALWriteFloatDataType = GDT_UInt32; + + return true; + } + + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Int32")) + { + m_bGDALCanWriteInt32 = true; + + m_GDALWriteIntDataType = GDT_Int32; + m_lGDALMaxCanWrite = INT32_MAX; + m_lGDALMinCanWrite = INT32_MIN; + + if (! m_bGDALCanWriteFloat) + m_GDALWriteFloatDataType = GDT_Int32; + + return true; + } + + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "UInt16")) + { + m_bGDALCanWriteInt32 = false; + + m_GDALWriteIntDataType = GDT_UInt16; + m_lGDALMaxCanWrite = UINT16_MAX; + m_lGDALMinCanWrite = 0; + + if (! m_bGDALCanWriteFloat) + m_GDALWriteFloatDataType = GDT_UInt16; + + return true; + } + + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Int16")) + { + m_bGDALCanWriteInt32 = false; + + m_GDALWriteIntDataType = GDT_Int16; + m_lGDALMaxCanWrite = INT16_MAX; + m_lGDALMinCanWrite = INT16_MIN; + + if (! m_bGDALCanWriteFloat) + m_GDALWriteFloatDataType = GDT_Int16; + + return true; + } + + if (strstr(CSLFetchNameValue(papszMetadata, "DMD_CREATIONDATATYPES"), "Byte")) + { + m_bGDALCanWriteInt32 = false; + + m_GDALWriteIntDataType = GDT_Byte; + m_lGDALMaxCanWrite = UINT8_MAX; + m_lGDALMinCanWrite = 0; + + if (! m_bGDALCanWriteFloat) + m_GDALWriteFloatDataType = GDT_Byte; + + return true; + } + + // This driver does not even support byte output + cerr << ERR << "Cannot write using raster GDAL driver '" << m_strRasterGISOutFormat << ", not even byte output is supported'. Choose another GIS raster format." << endl; + return false; +} + +//=============================================================================================================================== +//! Checks whether the selected vector GDAL/OGR driver supports file creation etc. +//=============================================================================================================================== +bool CSimulation::bCheckVectorGISOutputFormat(void) +{ + // Load the vector GDAL driver (this assumes that GDALAllRegister() has already been called) + GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName(m_strVectorGISOutFormat.c_str()); + + if (NULL == pDriver) + { + // Can't load vector GDAL driver. Incorrectly specified? + cerr << ERR << "Unknown vector GIS output format '" << m_strVectorGISOutFormat << "'." << endl; + return false; + } + + // Get the metadata for this vector driver + char **papszMetadata = pDriver->GetMetadata(); + + // For GDAL2, need to test if this is a vector driver + if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_VECTOR, false)) + { + // This is not a vector driver + cerr << ERR << "GDAL driver '" << m_strVectorGISOutFormat << "' is not a vector driver. Choose another format." << endl; + return false; + } + + if (!CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATE, false)) + { + // Driver does not support create() method + cerr << ERR << "Cannot write vector GIS files using GDAL driver '" << m_strRasterGISOutFormat << "'. Choose another format." << endl; + return false; + } + + // Driver is OK, now set some options for individual drivers + if (m_strVectorGISOutFormat == "ESRI Shapefile") + { + // Set this, so that just a single dataset-with-one-layer shapefile is created, rather than a directory (see http://www.gdal.org/ogr/drv_shapefile.html) + m_strOGRVectorOutputExtension = ".shp"; + } + + else if (m_strVectorGISOutFormat == "geojson") + { + m_strOGRVectorOutputExtension = ".geojson"; + } + + else if (m_strVectorGISOutFormat == "gpkg") + { + m_strOGRVectorOutputExtension = ".gpkg"; + } + + // TODO 033 Others + + return true; +} + +//=============================================================================================================================== +//! The bSaveAllRasterGISFiles member function saves the raster GIS files using values from the RasterGrid array +//=============================================================================================================================== +bool CSimulation::bSaveAllRasterGISFiles(void) +{ + // Increment file number + m_nGISSave++; + + // Set for next save + if (m_bSaveRegular) + { + m_dRegularSaveTime += m_dRegularSaveInterval; + } + else + { + if (m_nThisSave < m_nUSave - 1) + { + // Still have user-defined save times remaining + m_nThisSave++; + } + else + { + // Finished user-defined times, switch to regular interval using last value as interval + double dLastInterval; + + if (m_nUSave > 1) + dLastInterval = m_dUSaveTime[m_nUSave - 1] - m_dUSaveTime[m_nUSave - 2]; + else + dLastInterval = m_dUSaveTime[m_nUSave - 1]; + + m_dRegularSaveTime = m_dSimElapsed + dLastInterval; + m_dRegularSaveInterval = dLastInterval; + m_bSaveRegular = true; + } + } + + if (m_bSedIncTalusTopSurfSave) + if (! bWriteRasterGISFile(RASTER_PLOT_SED_TOP_INC_TALUS_ELEV, &RASTER_PLOT_SED_TOP_INC_TALUS_ELEV_TITLE)) + return false; + + if (m_bTopSurfIncSeaSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOP_ELEV_INC_SEA, &RASTER_PLOT_TOP_ELEV_INC_SEA_TITLE)) + return false; + + if (m_bTalusSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TALUS, &RASTER_PLOT_TALUS_TITLE)) + return false; + + if (m_bSlopeConsSedSave) + if (! bWriteRasterGISFile(RASTER_PLOT_CONS_SED_SLOPE, &RASTER_PLOT_CONS_SED_SLOPE_TITLE)) + return false; + + if (m_bSlopeSaveForCliffToe) + if (! bWriteRasterGISFile(RASTER_PLOT_SLOPE_FOR_CLIFF_TOE, &RASTER_PLOT_SLOPE_FOR_CLIFF_TOE_TITLE)) + return false; + + if (m_bCliffToeSave) + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_TOE, &RASTER_PLOT_CLIFF_TOE_TITLE)) + return false; + + if (m_bSeaDepthSave) + if (! bWriteRasterGISFile(RASTER_PLOT_SEA_DEPTH, &RASTER_PLOT_SEA_DEPTH_TITLE)) + return false; + + if (m_bWaveHeightSave) + if (! bWriteRasterGISFile(RASTER_PLOT_WAVE_HEIGHT, &RASTER_PLOT_WAVE_HEIGHT_TITLE)) + return false; + + if (m_bWaveAngleSave) + if (! bWriteRasterGISFile(RASTER_PLOT_WAVE_ORIENTATION, &RASTER_PLOT_WAVE_ORIENTATION_TITLE)) + return false; + + // Don't write platform erosion files if there is no platform erosion + if (m_bDoShorePlatformErosion) + { + if (m_bPotentialPlatformErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_PLATFORM_EROSION, &RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_TITLE)) + return false; + + if (m_bActualPlatformErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_ACTUAL_PLATFORM_EROSION, &RASTER_PLOT_ACTUAL_PLATFORM_EROSION_TITLE)) + return false; + + if (m_bTotalPotentialPlatformErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION, &RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION_TITLE)) + return false; + + if (m_bTotalActualPlatformErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION, &RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION_TITLE)) + return false; + + if (m_bPotentialPlatformErosionMaskSave) + if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK, &RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK_TITLE)) + return false; + + if (m_bBeachProtectionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_PROTECTION, &RASTER_PLOT_BEACH_PROTECTION_TITLE)) + return false; + + if (m_bPotentialBeachErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_POTENTIAL_BEACH_EROSION, &RASTER_PLOT_POTENTIAL_BEACH_EROSION_TITLE)) + return false; + + if (m_bActualBeachErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_ACTUAL_BEACH_EROSION, &RASTER_PLOT_ACTUAL_BEACH_EROSION_TITLE)) + return false; + + if (m_bTotalPotentialBeachErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION, &RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION_TITLE)) + return false; + + if (m_bTotalActualBeachErosionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION, &RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION_TITLE)) + return false; + + if (m_bBeachDepositionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_DEPOSITION, &RASTER_PLOT_BEACH_DEPOSITION_TITLE)) + return false; + + if (m_bTotalBeachDepositionSave) + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_BEACH_DEPOSITION, &RASTER_PLOT_TOTAL_BEACH_DEPOSITION_TITLE)) + return false; + } + + if (m_bLandformSave) + if (! bWriteRasterGISFile(RASTER_PLOT_LANDFORM, &RASTER_PLOT_LANDFORM_TITLE)) + return false; + + if (m_bAvgWaveHeightSave) + if (! bWriteRasterGISFile(RASTER_PLOT_AVG_WAVE_HEIGHT, &RASTER_PLOT_AVG_WAVE_HEIGHT_TITLE)) + return false; + + if (m_bAvgWaveAngleSave) + if (! bWriteRasterGISFile(RASTER_PLOT_AVG_WAVE_ORIENTATION, &RASTER_PLOT_AVG_WAVE_ORIENTATION_TITLE)) + return false; + + if (m_bAvgSeaDepthSave) + if (! bWriteRasterGISFile(RASTER_PLOT_AVG_SEA_DEPTH, &RASTER_PLOT_AVG_SEA_DEPTH_TITLE)) + return false; + + if (m_bSedimentInput && m_bSedimentInputEventSave) + if (! bWriteRasterGISFile(RASTER_PLOT_SEDIMENT_INPUT, &RASTER_PLOT_SEDIMENT_INPUT_EVENT_TITLE)) + return false; + + // Don't write suspended sediment files if there is no fine sediment + if (m_bHaveFineSediment) + { + if (m_bSuspSedSave) + if (! bWriteRasterGISFile(RASTER_PLOT_SUSPENDED_SEDIMENT, &RASTER_PLOT_SUSPENDED_SEDIMENT_TITLE)) + return false; + + if (m_bAvgSuspSedSave) + if (! bWriteRasterGISFile(RASTER_PLOT_AVG_SUSPENDED_SEDIMENT, &RASTER_PLOT_AVG_SUSPENDED_SEDIMENT_TITLE)) + return false; + } + + if (m_bBasementElevSave) + if (! bWriteRasterGISFile(RASTER_PLOT_BASEMENT_ELEVATION, &RASTER_PLOT_BASEMENT_ELEVATION_TITLE)) + return false; + + for (int nLayer = 0; nLayer < m_nLayers; nLayer++) + { + if (m_bHaveFineSediment && m_bFineUnconsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + + if (m_bHaveSandSediment && m_bSandUnconsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + + if (m_bHaveCoarseSediment && m_bCoarseUnconsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT, &RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + + if (m_bHaveFineSediment && m_bFineConsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + + if (m_bHaveSandSediment && m_bSandConsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + + if (m_bHaveCoarseSediment && m_bCoarseConsSedSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT, &RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT_TITLE, nLayer)) + return false; + } + } + + if (m_bSliceSave) + { + for (int i = 0; i < static_cast(m_VdSliceElev.size()); i++) + { + if (! bWriteRasterGISFile(RASTER_PLOT_SLICE, &RASTER_PLOT_SLICE_TITLE, 0, m_VdSliceElev[i])) + return false; + } + } + + if (m_bRasterCoastlineSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_COAST, &RASTER_PLOT_COAST_TITLE)) + return false; + } + + if (m_bRasterNormalProfileSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_NORMAL_PROFILE, &RASTER_PLOT_NORMAL_PROFILE_TITLE)) + return false; + } + + if (m_bActiveZoneSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_ACTIVE_ZONE, &RASTER_PLOT_ACTIVE_ZONE_TITLE)) + return false; + } + + // Don't write cliff collapse files if we aren't considering cliff collapse + if (m_bDoCliffCollapse) + { + if (m_bCliffCollapseSave) + { + if (m_bHaveFineSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE_TITLE)) + return false; + } + + if (m_bHaveSandSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND_TITLE)) + return false; + } + + if (m_bHaveCoarseSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE, &RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE_TITLE)) + return false; + } + + if (m_bCliffNotchAllSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_NOTCH_ALL, &RASTER_PLOT_CLIFF_NOTCH_ALL_TITLE)) + return false; + } + +#ifdef _DEBUG + if (m_bCliffCollapseTimestepSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP, &RASTER_PLOT_CLIFF_COLLAPSE_TIMESTEP_TITLE)) + return false; + } +#endif + } + + if (m_bTotCliffCollapseSave) + { + if (m_bHaveFineSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE_TITLE)) + return false; + } + + if (m_bHaveSandSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND_TITLE)) + return false; + } + + if (m_bHaveCoarseSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE_TITLE)) + return false; + } + } + + if (m_bCliffCollapseDepositionSave) + { + if (m_bHaveSandSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND, &RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE)) + return false; + } + + if (m_bHaveCoarseSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE, &RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE)) + return false; + } + } + + if (m_bTotCliffCollapseDepositionSave) + { + if (m_bHaveSandSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND_TITLE)) + return false; + } + + if (m_bHaveCoarseSediment) + { + if (! bWriteRasterGISFile(RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE, &RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE_TITLE)) + return false; + } + } + } + + if (m_bRasterPolygonSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON, &RASTER_PLOT_POLYGON_TITLE)) + return false; + } + + if (m_bSeaMaskSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_INUNDATION_MASK, &RASTER_PLOT_INUNDATION_MASK_TITLE)) + return false; + } + + if (m_bBeachMaskSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_BEACH_MASK, &RASTER_PLOT_BEACH_MASK_TITLE)) + return false; + } + + if (m_bInterventionClassSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_INTERVENTION_CLASS, &RASTER_PLOT_INTERVENTION_CLASS_TITLE)) + return false; + } + + if (m_bInterventionHeightSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_INTERVENTION_HEIGHT, &RASTER_PLOT_INTERVENTION_HEIGHT_TITLE)) + return false; + } + + if (m_bShadowZoneCodesSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_SHADOW_ZONE, &RASTER_PLOT_SHADOW_ZONE_TITLE)) + return false; + + if (! bWriteRasterGISFile(RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE, &RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE_TITLE)) + return false; + } + + if (m_bDeepWaterWaveAngleSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION, &RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION_TITLE)) + return false; + } + + if (m_bDeepWaterWaveHeightSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT, &RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT_TITLE)) + return false; + } + + if (m_bDeepWaterWavePeriodSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_DEEP_WATER_WAVE_PERIOD, &RASTER_PLOT_DEEP_WATER_WAVE_PERIOD_TITLE)) + return false; + } + + if (m_bPolygonUnconsSedUpOrDownDriftSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT, &RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT_TITLE)) + return false; + } + + if (m_bPolygonUnconsSedGainOrLossSave) + { + if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON_GAIN_OR_LOSS, &RASTER_PLOT_POLYGON_GAIN_OR_LOSS_TITLE)) + return false; + } + + // if (m_bSetupSurgeFloodMaskSave) + // { + // if (! bWriteRasterGISFile(RASTER_PLOT_SETUP_SURGE_FLOOD_MASK, &RASTER_PLOT_SETUP_SURGE_FLOOD_MASK_TITLE)) + // return false; + // } + + // if (m_bSetupSurgeRunupFloodMaskSave) + // { + // if (! bWriteRasterGISFile(RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK, &RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK_TITLE)) + // return false; + // } + // + return true; +} + +//=============================================================================================================================== +//! The bSaveAllvectorGISFiles member function saves the vector GIS files TODO 081 Choose more files to omit from "usual" vector output +//=============================================================================================================================== +bool CSimulation::bSaveAllVectorGISFiles(void) +{ + // Always written + if (m_bCoastSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_COAST, &VECTOR_PLOT_COAST_TITLE)) + return false; + + if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_SWL_HIGHEST, &VECTOR_PLOT_COAST_SWL_HIGHEST_TITLE)) + return false; + + if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_SWL_LOWEST, &VECTOR_PLOT_COAST_SWL_LOWEST_TITLE)) + return false; + } + + if (m_bCliffEdgeSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_CLIFF_EDGE, &VECTOR_PLOT_CLIFF_EDGE_TITLE)) + return false; + } + + if (m_bNormalsSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_NORMALS, &VECTOR_PLOT_NORMALS_TITLE)) + return false; + } + + if (m_bInvalidNormalsSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_INVALID_NORMALS, &VECTOR_PLOT_INVALID_NORMALS_TITLE)) + return false; + } + + if (m_bCoastCurvatureSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_COAST_CURVATURE, &VECTOR_PLOT_COAST_CURVATURE_TITLE)) + return false; + } + + if (m_bWaveAngleAndHeightSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_WAVE_ANGLE_AND_HEIGHT_TITLE)) + return false; + } + + if (m_bAvgWaveAngleAndHeightSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_AVG_WAVE_ANGLE_AND_HEIGHT_TITLE)) + return false; + } + + if (m_bWaveEnergySinceCollapseSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE, &VECTOR_PLOT_WAVE_ENERGY_SINCE_COLLAPSE_TITLE)) + return false; + } + + if (m_bMeanWaveEnergySave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_MEAN_WAVE_ENERGY, &VECTOR_PLOT_MEAN_WAVE_ENERGY_TITLE)) + return false; + } + + if (m_bBreakingWaveHeightSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_BREAKING_WAVE_HEIGHT, &VECTOR_PLOT_BREAKING_WAVE_HEIGHT_TITLE)) + return false; + } + + if (m_bPolygonNodeSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_NODES, &VECTOR_PLOT_POLYGON_NODES_TITLE)) + return false; + } + + if (m_bPolygonBoundarySave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_BOUNDARY, &VECTOR_PLOT_POLYGON_BOUNDARY_TITLE)) + return false; + } + + if (m_bCliffNotchSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_CLIFF_NOTCH_ACTIVE, &VECTOR_PLOT_CLIFF_NOTCH_ACTIVE_TITLE)) + return false; + } + + if (m_bWaveTransectPointsSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_TRANSECT_POINTS, &VECTOR_PLOT_WAVE_TRANSECT_POINTS_TITLE)) + return false; + } + + if (m_bShadowBoundarySave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_SHADOW_ZONE_BOUNDARY, &VECTOR_PLOT_SHADOW_ZONE_BOUNDARY_TITLE)) + return false; + } + + if (m_bShadowDowndriftBoundarySave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY, &VECTOR_PLOT_DOWNDRIFT_ZONE_BOUNDARY_TITLE)) + return false; + } + + if (m_bDeepWaterWaveAngleAndHeightSave) + { + if (! bWriteVectorGISFile(VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT, &VECTOR_PLOT_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_TITLE)) + return false; + } + + // if (m_bWaveSetupSave) + // { + // if (! bWriteVectorGISFile(VECTOR_PLOT_WAVE_SETUP, &VECTOR_PLOT_WAVE_SETUP_TITLE)) + // return false; + // } + // + // if (m_bStormSurgeSave) + // { + // if (! bWriteVectorGISFile(VECTOR_PLOT_STORM_SURGE, &VECTOR_PLOT_STORM_SURGE_TITLE)) + // return false; + // } + // + // if (m_bRunUpSave) + // { + // if (! bWriteVectorGISFile(VECTOR_PLOT_RUN_UP, &VECTOR_PLOT_RUN_UP_TITLE)) + // return false; + // } + // + // if (m_bRiverineFlooding && m_bVectorWaveFloodLineSave) + // { + // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_LINE, &VECTOR_PLOT_FLOOD_SWL_SETUP_LINE_TITLE)) + // return false; + // + // // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE, + // // &VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_LINE_TITLE)) return false; + // + // // if (! bWriteVectorGISFile(VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE, + // // &VECTOR_PLOT_FLOOD_SWL_SETUP_SURGE_RUNUP_LINE_TITLE)) return false; + // } + + return true; +} + +//=============================================================================================================================== +//! Finds the max and min values in order to scale raster output if we cannot write doubles +//=============================================================================================================================== +void CSimulation::GetRasterOutputMinMax(int const nDataItem, double& dMin, double& dMax, int const nLayer, double const dElev) +{ + // If this is a binary mask layer, we already know the max and min values + if ((nDataItem == RASTER_PLOT_POTENTIAL_PLATFORM_EROSION_MASK) || + (nDataItem == RASTER_PLOT_INUNDATION_MASK) || + (nDataItem == RASTER_PLOT_BEACH_MASK) || + (nDataItem == RASTER_PLOT_COAST) || + (nDataItem == RASTER_PLOT_NORMAL_PROFILE) || + (nDataItem == RASTER_PLOT_ACTIVE_ZONE) || + (nDataItem == RASTER_PLOT_POLYGON_UPDRIFT_OR_DOWNDRIFT) || + (nDataItem == RASTER_PLOT_SETUP_SURGE_FLOOD_MASK) || + (nDataItem == RASTER_PLOT_SETUP_SURGE_RUNUP_FLOOD_MASK) || + (nDataItem == RASTER_PLOT_WAVE_FLOOD_LINE)) + { + dMin = 0; + dMax = 1; + + return; + } + + // Not a binary mask layer, so we must find the max and min values + dMin = DBL_MAX; + dMax = DBL_MIN; + + double dTmp = 0; + + for (int nY = 0; nY < m_nYGridSize; nY++) + { + for (int nX = 0; nX < m_nXGridSize; nX++) + { + switch (nDataItem) + { + case (RASTER_PLOT_SLICE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetLayerAtElev(dElev); + break; + + case (RASTER_PLOT_LANDFORM): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); + break; + + case (RASTER_PLOT_INTERVENTION_CLASS): + dTmp = INT_NODATA; + + if (bIsInterventionCell(nX, nY)) + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); + + break; + + case (RASTER_PLOT_INTERVENTION_HEIGHT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetInterventionHeight(); + break; + + case (RASTER_PLOT_POLYGON): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + break; + + case (RASTER_PLOT_BASEMENT_ELEVATION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBasementElev(); + break; + + case (RASTER_PLOT_SED_TOP_INC_TALUS_ELEV): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetAllSedTopElevIncTalus(); + break; + + case (RASTER_PLOT_TALUS): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTalusDepth(); + break; + + case (RASTER_PLOT_TOP_ELEV_INC_SEA): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTopElevIncSea(); + break; + + case (RASTER_PLOT_CONS_SED_SLOPE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetConsSedSlope(); + break; + + case (RASTER_PLOT_SEA_DEPTH): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSeaDepth(); + break; + + case (RASTER_PLOT_AVG_SEA_DEPTH): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSeaDepth() / static_cast(m_ulIter); + break; + + case (RASTER_PLOT_WAVE_HEIGHT): + if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) + dTmp = m_dMissingValue; + else + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); + + break; + + case (RASTER_PLOT_AVG_WAVE_HEIGHT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveHeight() / static_cast(m_ulIter); + break; + + case (RASTER_PLOT_WAVE_ORIENTATION): + if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) + dTmp = m_dMissingValue; + + else + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); + + break; + + case (RASTER_PLOT_AVG_WAVE_ORIENTATION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotWaveAngle() / static_cast(m_ulIter); + break; + + case (RASTER_PLOT_BEACH_PROTECTION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachProtectionFactor(); + + if (! bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) + dTmp = 1 - dTmp; // Output the inverse, seems more intuitive + + break; + + case (RASTER_PLOT_POTENTIAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialPlatformErosion(); + break; + + case (RASTER_PLOT_ACTUAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualPlatformErosion(); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialPlatformErosion(); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_PLATFORM_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualPlatformErosion(); + break; + + case (RASTER_PLOT_POTENTIAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetPotentialBeachErosion(); + break; + + case (RASTER_PLOT_ACTUAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetActualBeachErosion(); + break; + + case (RASTER_PLOT_TOTAL_POTENTIAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotPotentialBeachErosion(); + break; + + case (RASTER_PLOT_TOTAL_ACTUAL_BEACH_EROSION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotActualBeachErosion(); + break; + + case (RASTER_PLOT_BEACH_DEPOSITION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetBeachDeposition(); + break; + + case (RASTER_PLOT_TOTAL_BEACH_DEPOSITION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotBeachDeposition(); + break; + + case (RASTER_PLOT_SUSPENDED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetSuspendedSediment(); + break; + + case (RASTER_PLOT_AVG_SUSPENDED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSuspendedSediment() / + static_cast(m_ulIter); + break; + + case (RASTER_PLOT_FINE_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetFineDepth(); + break; + + case (RASTER_PLOT_SAND_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetSandDepth(); + break; + + case (RASTER_PLOT_COARSE_UNCONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetUnconsolidatedSediment()->dGetCoarseDepth(); + break; + + case (RASTER_PLOT_FINE_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetFineDepth(); + break; + + case (RASTER_PLOT_SAND_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetSandDepth(); + break; + + case (RASTER_PLOT_COARSE_CONSOLIDATED_SEDIMENT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].pGetLayerAboveBasement(nLayer)->pGetConsolidatedSediment()->dGetCoarseDepth(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_FINE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionFine(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionSand(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_EROSION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseErosionCoarse(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_FINE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseFine(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseSand(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_EROSION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapseCoarse(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseSandTalusDeposition(); + break; + + case (RASTER_PLOT_CLIFF_COLLAPSE_DEPOSITION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetThisIterCliffCollapseCoarseTalusDeposition(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_SAND): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotSandTalusDeposition(); + break; + + case (RASTER_PLOT_TOTAL_CLIFF_COLLAPSE_DEPOSITION_COARSE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].dGetTotCoarseTalusDeposition(); + break; + + case (RASTER_PLOT_SHADOW_ZONE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber(); + break; + + case (RASTER_PLOT_SHADOW_DOWNDRIFT_ZONE): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_ORIENTATION): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); + break; + + case (RASTER_PLOT_DEEP_WATER_WAVE_HEIGHT): + dTmp = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber(); + break; + + case (RASTER_PLOT_POLYGON_GAIN_OR_LOSS): + int const nPoly = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + int const nPolyCoast = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonCoastID(); + + if (nPoly == INT_NODATA) + dTmp = m_dMissingValue; + else + dTmp = m_VCoast[nPolyCoast].pGetPolygon(nPoly)->dGetBeachDepositionAndSuspensionAllUncons(); + + break; + } + + if (! bFPIsEqual(dTmp, DBL_NODATA, TOLERANCE)) + { + if (dTmp > dMax) + dMax = dTmp; + + if (dTmp < dMin) + dMin = dTmp; + } + } + } +} + +//=============================================================================================================================== +//! Sets per-driver defaults for raster files created using GDAL +//=============================================================================================================================== +void CSimulation::SetRasterFileCreationDefaults(void) +{ + string const strDriver = strToLower(&m_strRasterGISOutFormat); + string const strComment = "Created by " + PROGRAM_NAME + " for " + PLATFORM + " " + strGetBuild() + " running on " + strGetComputerName(); + + // TODO 034 Do these for all commonly-used file types + if (strDriver == "aaigrid") + { + } + else if (strDriver == "bmp") + { + } + else if (strDriver == "gtiff") + { + if (m_bWorldFile) + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "TFW", "YES"); + + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NUM_THREADS", "ALL_CPUS"); + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMPRESS", "LZW"); + } + else if (strDriver == "hfa") + { + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NBITS", "4"); + } + else if (strDriver == "jpeg") + { + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "QUALITY", "95"); + } + else if (strDriver == "png") + { + if (m_bWorldFile) + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "WORLDFILE", "YES"); + + // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "TITLE", "This is the title"); m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "DESCRIPTION", "This is a description"); + // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COPYRIGHT", "This is some copyright statement"); + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "NBITS", "4"); + } + else if (strDriver == "rst") + { + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); + } + else if (strDriver == "geojson") + { + // m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "COMMENT", strComment.c_str()); + } + else if (strDriver == "gpkg") + { + // TODO 065 Does GDAL support overwriting raster gpkg files yet? + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "OVERWRITE", "YES"); + m_papszGDALRasterOptions = CSLSetNameValue(m_papszGDALRasterOptions, "USE_TILE_EXTENT", "YES"); + } + else if (strDriver == "netcdf") + { + } +} + +//=============================================================================================================================== +//! Returns the opposite direction +//=============================================================================================================================== +int CSimulation::nGetOppositeDirection(int const nDirection) +{ + switch (nDirection) + { + case NORTH: + return SOUTH; + + case NORTH_EAST: + return SOUTH - WEST; + + case EAST: + return WEST; + + case SOUTH_EAST: + return NORTH - WEST; + + case SOUTH: + return NORTH; + + case SOUTH_WEST: + return NORTH - EAST; + + case WEST: + return EAST; + + case NORTH_WEST: + return SOUTH - EAST; + } + + // Should never get here + return NO_DIRECTION; +} + +// //=============================================================================================================================== +// //! Given two integer points, calculates the slope and intercept of the line passing through the points +// //=============================================================================================================================== +// void CSimulation::GetSlopeAndInterceptFromPoints(CGeom2DIPoint const* pPti1, CGeom2DIPoint const* pPti2, double& dSlope, double& dIntercept) +// { +// int nX1 = pPti1->nGetX(); +// int nY1 = pPti1->nGetY(); +// int nX2 = pPti2->nGetX(); +// int nY2 = pPti2->nGetY(); +// +// double dXDiff = nX1 - nX2; +// double dYDiff = nY1 - nY2; +// +// if (bFPIsEqual(dXDiff, 0.0, TOLERANCE)) +// dSlope = 0; +// else +// dSlope = dYDiff / dXDiff; +// +// dIntercept = nY1 - (dSlope * nX1); +// } + +//=============================================================================================================================== +//! Finds the closest point on any coastline to a given point +//=============================================================================================================================== +CGeom2DIPoint CSimulation::PtiFindClosestCoastPoint(int const nX, int const nY, int& nCoastFound) +{ + unsigned int nMinSqDist = UINT_MAX; + CGeom2DIPoint PtiCoastPoint; + + // Do for every coast + for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) + { + for (int j = 0; j < m_VCoast[nCoast].nGetCoastlineSize(); j++) + { + // Get the coords of the grid cell marked as coastline for the coastal landform object + int const nXCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetX(); + int const nYCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetY(); + + // Calculate the squared distance between this point and the given point + int const nXDist = nX - nXCoast; + int const nYDist = nY - nYCoast; + + unsigned int const nSqDist = (nXDist * nXDist) + (nYDist * nYDist); + + // Is this the closest so dar? + if (nSqDist < nMinSqDist) + { + nMinSqDist = nSqDist; + PtiCoastPoint.SetXY(nXCoast, nYCoast); + nCoastFound = nCoast; + } + } + } + + return PtiCoastPoint; +} + +//=============================================================================================================================== +//! Finds the number of the closest point on any coastline to a given point, or INT_NODATA in case of error +//=============================================================================================================================== +int CSimulation::nFindClosestCoastPoint(int const nX, int const nY, int& nCoastFound) +{ + unsigned int nMinSqDist = UINT_MAX; + int nCoastPoint = INT_NODATA; + + // Do for every coast + for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) + { + for (int j = 0; j < m_VCoast[nCoast].nGetCoastlineSize(); j++) + { + // Get the coords of the grid cell marked as coastline for the coastal landform object + int const nXCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetX(); + int const nYCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(j)->nGetY(); + + // Calculate the squared distance between this point and the given point + int const nXDist = nX - nXCoast; + int const nYDist = nY - nYCoast; + + unsigned int const nSqDist = (nXDist * nXDist) + (nYDist * nYDist); + + // Is this the closest so dar? + if (nSqDist < nMinSqDist) + { + nMinSqDist = nSqDist; + nCoastPoint = j; + nCoastFound = nCoast; + } + } + } + + return nCoastPoint; +} + +//=============================================================================================================================== +//! Given a length in m, this returns the rounded equivalent number of cells +//=============================================================================================================================== +int CSimulation::nConvertMetresToNumCells(double const dLen) const +{ + return nRound(dLen / m_dCellSide); +} + +//=============================================================================================================================== +//! For the straight line between two points A and B, and given another point C, this finds the closest point on the line A-B to the point C. From https://cboard.cprogramming.com/c-programming/155809-find-closest-point-line.html +//=============================================================================================================================== +void CSimulation::FindClosestPointOnStraightLine(double const dAx, double const dAy, double const dBx, double const dBy, double const dPx, double const dPy, double& dXRet, double& dYRet) +{ + double const dAPx = dPx - dAx; + double const dAPy = dPy - dAy; + double const dABx = dBx - dAx; + double const dABy = dBy - dAy; + double const dMagAB2 = (dABx * dABx) + (dABy * dABy); + double const dABdotAP = (dABx * dAPx) + (dABy * dAPy); + double const dT = dABdotAP / dMagAB2; + + if ( dT < 0) + { + dXRet = dAx; + dYRet = dAy; + } + else if (dT > 1) + { + dXRet = dBx; + dYRet = dBy; + } + else + { + dXRet = dAx + (dABx * dT); + dYRet = dAy + (dABy * dT); + } +} + +//=============================================================================================================================== +//! Given two different edge cells, returns true if they are adjacent +//=============================================================================================================================== +bool CSimulation::bIsAdjacentEdgeCell(CGeom2DIPoint const* pPt1, CGeom2DIPoint const* pPt2) +{ + int const nX1 = pPt1->nGetX(); + int const nY1 = pPt1->nGetY(); + int const nX2 = pPt2->nGetX(); + int const nY2 = pPt2->nGetY(); + + if (nX1 == nX2) + { + if ((nY1 == nY2 + 1) || (nY1 == nY2 - 1)) + return true; + } + + if (nY1 == nY2) + { + if ((nX1 == nX2 + 1) || (nX1 == nX2 - 1)) + return true; + } + + return false; +} diff --git a/src/hermite_cubic.h b/src/hermite_cubic.h index dbe0bbf10..780ce4777 100644 --- a/src/hermite_cubic.h +++ b/src/hermite_cubic.h @@ -1,26 +1,26 @@ -/*! - \brief Definitions of some routines from the hermite_cubic library - \details TODO 001 This is a more detailed description of the hermite_cubic routines. - \author John Burkardt - \author Modified by David Favis-Mortlock and Andres Payo - \date 2025 - \copyright GNU Lesser General Public License - \file hermite_cubic.h - \brief Contains definitions of hermite-cubic routines -*/ - -#ifndef HERMITE_H -#define HERMITE_H -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -void r8vec_bracket3(int const, double const*, double const, int*); -void hermite_cubic_value(double const, double const, double const, double const, double const, double const, int const, double const*, double* const, double* const, double* const, double* const); -void hermite_cubic_spline_value(int const, double* const, double* const, double* const, int const, double* const, double*, double*, double*, double*); -#endif // HERMITE_H +/*! + \brief Definitions of some routines from the hermite_cubic library + \details TODO 001 This is a more detailed description of the hermite_cubic routines. + \author John Burkardt + \author Modified by David Favis-Mortlock and Andres Payo + \date 2025 + \copyright GNU Lesser General Public License + \file hermite_cubic.h + \brief Contains definitions of hermite-cubic routines +*/ + +#ifndef HERMITE_H +#define HERMITE_H +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +void r8vec_bracket3(int const, double const*, double const, int*); +void hermite_cubic_value(double const, double const, double const, double const, double const, double const, int const, double const*, double* const, double* const, double* const, double* const); +void hermite_cubic_spline_value(int const, double* const, double* const, double* const, int const, double* const, double*, double*, double*, double*); +#endif // HERMITE_H diff --git a/src/run_cmake.sh b/src/run_cmake.sh index d7bf12379..e3f1e30cd 100755 --- a/src/run_cmake.sh +++ b/src/run_cmake.sh @@ -19,10 +19,10 @@ while getopts 'civh' flag; do done # Change this to change build type -#buildtype=DEBUG -buildtype=RELEASE -# buildtype=PRERELEASE -# buildtype=RELWITHDEBINFO # Not yet implemented in CMakeLists.txt +buildtype=DEBUG +#buildtype=RELEASE +#buildtype=PRERELEASE +#buildtype=RELWITHDEBINFO #buildtype=MINSIZEREL # Not yet implemented in CMakeLists.txt #buildtype=GCOV #buildtype=CALLGRIND @@ -76,14 +76,14 @@ if [ "$cflag" = "true" ]; then fi # CMAKE_COMPILER_ARGS="PKG_CPPFLAGS='-Xclang -fopenmp' PKG_LIBS=-lomp" # cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout $CMAKE_COMPILER_ARGS . -#cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout -DCMAKE_VERBOSE_MAKEFILE=ON $CMAKE_COMPILER_ARGS . +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout -DCMAKE_VERBOSE_MAKEFILE=ON $CMAKE_COMPILER_ARGS . #cmake -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout $CMAKE_COMPILER_ARGS . -G"CodeBlocks - Unix Makefiles" # Or Ninja? -cmake -G Ninja -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout $CMAKE_COMPILER_ARGS . +#cmake -G Ninja -DCMAKE_BUILD_TYPE=$buildtype -DCOMPILER=$compiler -DCSHORE_LIBRARY=$cshorelibrary -DCSHORE_INOUT=$cshoreinout $CMAKE_COMPILER_ARGS . if [ "$iflag" = "true" ]; then - # make install - ninja install #>output.txt + make install +# ninja install #>output.txt if [[ $OSTYPE == 'darwin'* ]]; then # Let's sign to enable profiling codesign -s - -f --entitlements ../debug.plist ../cme diff --git a/src/simulation.cpp b/src/simulation.cpp index 0b32cf182..4bf896f7c 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -1,1369 +1,1369 @@ -/*! - \file simulation.cpp - \brief The start-of-simulation routine - \details TODO 001 A more detailed description of this routine. - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* ============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -==============================================================================================================================*/ -#include - -#include -#include -#include - -#include - -#include -using std::fixed; - -#include -using std::cerr; -using std::cin; -using std::endl; -using std::ios; - -#include -using std::setprecision; - -#include - -#include -using std::to_string; - -#include // C++17 and later, needed for missing output directory creation -using std::filesystem::is_directory; -using std::filesystem::exists; -using std::filesystem::create_directories; - -#include - -#include "cme.h" -#include "simulation.h" -#include "raster_grid.h" -#include "coast.h" - -//=============================================================================================================================== -//! The CSimulation constructor -//=============================================================================================================================== -CSimulation::CSimulation(void) -{ - // Initialization - m_bHaveFineSediment = false; - m_bHaveSandSediment = false; - m_bHaveCoarseSediment = false; - m_bBasementElevSave = false; - m_bSedIncTalusTopSurfSave = false; - m_bTopSurfIncSeaSave = false; - m_bTalusSave = false; - m_bSliceSave = false; - m_bSeaDepthSave = false; - m_bAvgSeaDepthSave = false; - m_bWaveHeightSave = false; - m_bAvgWaveHeightSave = false; - m_bWaveAngleSave = false; - m_bAvgWaveAngleSave = false; - m_bWaveAngleAndHeightSave = false; - m_bAvgWaveAngleAndHeightSave = false; - m_bDeepWaterWaveAngleAndHeightSave = false; - m_bBeachProtectionSave = false; - m_bWaveEnergySinceCollapseSave = false; - m_bMeanWaveEnergySave = false; - m_bBreakingWaveHeightSave = false; - m_bPotentialPlatformErosionSave = false; - m_bActualPlatformErosionSave = false; - m_bTotalPotentialPlatformErosionSave = false; - m_bTotalActualPlatformErosionSave = false; - m_bPotentialBeachErosionSave = false; - m_bActualBeachErosionSave = false; - m_bTotalPotentialBeachErosionSave = false; - m_bTotalActualBeachErosionSave = false; - m_bBeachDepositionSave = false; - m_bTotalBeachDepositionSave = false; - m_bLandformSave = false; - m_bSlopeConsSedSave = false; - m_bSlopeSaveForCliffToe = false; - m_bInterventionClassSave = false; - m_bInterventionHeightSave = false; - m_bSuspSedSave = false; - m_bAvgSuspSedSave = false; - m_bFineUnconsSedSave = false; - m_bSandUnconsSedSave = false; - m_bCoarseUnconsSedSave = false; - m_bFineConsSedSave = false; - m_bSandConsSedSave = false; - m_bCoarseConsSedSave = false; - m_bRasterCoastlineSave = false; - m_bRasterNormalProfileSave = false; - m_bActiveZoneSave = false; - m_bCliffCollapseSave = false; - m_bTotCliffCollapseSave = false; - m_bCliffCollapseDepositionSave = false; - m_bTotCliffCollapseDepositionSave = false; - m_bCliffNotchAllSave = false; - m_bCliffCollapseTimestepSave = false; - m_bRasterPolygonSave = false; - m_bPotentialPlatformErosionMaskSave = false; - m_bSeaMaskSave = false; - m_bBeachMaskSave = false; - m_bShadowZoneCodesSave = false; - m_bSaveRegular = false; - m_bCoastSave = false; - m_bCliffEdgeSave = false; - m_bNormalsSave = false; - m_bInvalidNormalsSave = false; - m_bCoastCurvatureSave = false; - m_bPolygonNodeSave = false; - m_bPolygonBoundarySave = false; - m_bCliffNotchSave = false; - m_bShadowBoundarySave = false; - m_bShadowDowndriftBoundarySave = false; - m_bDeepWaterWaveAngleSave = false; - m_bDeepWaterWaveHeightSave = false; - m_bDeepWaterWavePeriodSave = false; - m_bPolygonUnconsSedUpOrDownDriftSave = false; - m_bPolygonUnconsSedGainOrLossSave = false; - m_bCliffToeSave = false; - m_bSeaAreaTSSave = false; - m_bSWLTSSave = false; - m_bActualPlatformErosionTSSave = false; - m_bSuspSedTSSave = false; - m_bFloodSetupSurgeTSSave = false; - m_bFloodSetupSurgeRunupTSSave = false; - m_bCliffCollapseDepositionTSSave = false; - m_bCliffCollapseErosionTSSave = false; - m_bCliffCollapseNetTSSave = false; - m_bBeachErosionTSSave = false; - m_bBeachDepositionTSSave = false; - m_bBeachSedimentChangeNetTSSave = false; - m_bCliffNotchElevTSSave = false; - m_bSaveGISThisIter = false; - m_bOutputConsolidatedProfileData = false; - m_bOutputParallelProfileData = false; - m_bOutputErosionPotentialData = false; - m_bOmitSearchNorthEdge = false; - m_bOmitSearchSouthEdge = false; - m_bOmitSearchWestEdge = false; - m_bOmitSearchEastEdge = false; - m_bDoShorePlatformErosion = false; - m_bDoCliffCollapse = false; - m_bDoBeachSedimentTransport = false; - m_bGDALCanWriteFloat = false; - m_bGDALCanWriteInt32 = false; - m_bScaleRasterOutput = false; - m_bWorldFile = false; - m_bSingleDeepWaterWaveValues = false; - m_bHaveWaveStationData = false; - m_bSedimentInput = false; - m_bSedimentInputAtPoint = false; - m_bSedimentInputAtCoast = false; - m_bSedimentInputAlongLine = false; - m_bSedimentInputThisIter = false; - m_bSedimentInputEventSave = false; - m_bWaveSetupSave = false; - m_bStormSurgeSave = false; - m_bRiverineFlooding = false; - m_bRunUpSave = false; - m_bSetupSurgeFloodMaskSave = false; - m_bSetupSurgeRunupFloodMaskSave = false; - m_bRasterWaveFloodLineSave = false; - m_bVectorWaveFloodLineSave = false; - m_bFloodLocationSave = false; - m_bFloodSWLSetupLineSave = false; - m_bFloodSWLSetupSurgeLine = false; - m_bFloodSWLSetupSurgeRunupLineSave = false; - m_bGISSaveDigitsSequential = false; - m_bHaveConsolidatedSediment = false; - m_bGDALOptimisations = false; - m_bCliffToeLocate = false; - m_bHighestSWLSoFar = false; - m_bLowestSWLSoFar = false; - - m_bGDALCanCreate = true; - m_bCSVPerTimestepResults = true; // Default to CSV output format - m_bYamlInputFormat = false; // Default to .dat format - - m_papszGDALRasterOptions = NULL; - m_papszGDALVectorOptions = NULL; - - m_nLayers = 0; - m_nCoastSmooth = 0; - m_nCoastSmoothingWindowSize = 0; - m_nSavGolCoastPoly = 0; - m_nCliffEdgeSmooth = 0; - m_nCliffEdgeSmoothWindow = 0; - m_nSavGolCliffEdgePoly = 0; - m_nProfileSmoothWindow = 0; - m_nCoastNormalSpacing = 0; - m_nCoastNormalInterventionSpacing = 0; - m_nCoastCurvatureInterval = 0; - m_nGISMaxSaveDigits = 0; - m_nGISSave = 0; - m_nUSave = 0; - m_nThisSave = 0; - m_nXGridSize = 0; - m_nYGridSize = 0; - m_nCoastMax = 0; - m_nCoastMin = 0; - m_nNumThisIterCliffCollapse = 0; - m_nNumTotCliffCollapse = 0; - m_nUnconsSedimentHandlingAtGridEdges = 0; - m_nBeachErosionDepositionEquation = 0; - m_nWavePropagationModel = 0; - m_nSimStartSec = 0; - m_nSimStartMin = 0; - m_nSimStartHour = 0; - m_nSimStartDay = 0; - m_nSimStartMonth = 0; - m_nSimStartYear = 0; - m_nDeepWaterWaveDataNumTimeSteps = 0; - m_nLogFileDetail = 0; - m_nRunUpEquation = 0; - m_nLevel = 0; - - // TODO 011 May wish to make this a user-supplied value - m_nGISMissingValue = INT_NODATA; - m_nMissingValue = INT_NODATA; - - m_nXMinBoundingBox = INT_MAX; - m_nXMaxBoundingBox = INT_MIN; - m_nYMinBoundingBox = INT_MAX; - m_nYMaxBoundingBox = INT_MIN; - - // cppcheck-suppress useInitializationList - m_GDALWriteIntDataType = GDT_Unknown; - // cppcheck-suppress useInitializationList - m_GDALWriteFloatDataType = GDT_Unknown; - - m_lGDALMaxCanWrite = 0; - m_lGDALMinCanWrite = 0; - - m_ulIter = 0; - m_ulTotTimestep = 0; - m_ulThisIterNumPotentialBeachErosionCells = 0; - m_ulThisIterNumActualBeachErosionCells = 0; - m_ulThisIterNumBeachDepositionCells = 0; - m_ulTotPotentialPlatformErosionOnProfiles = 0; - m_ulTotPotentialPlatformErosionBetweenProfiles = 0; - m_ulMissingValueBasementCells = 0; - m_ulNumCells = 0; - m_ulThisIterNumSeaCells = 0; - m_ulThisIterNumCoastCells = 0; - m_ulThisIterNumPotentialPlatformErosionCells = 0; - m_ulThisIterNumActualPlatformErosionCells = 0; - - m_ulMissingValue = UNSIGNED_LONG_NODATA; - - for (int i = 0; i < NUMBER_OF_RNGS; i++) - m_ulRandSeed[i] = 0; - - for (int i = 0; i < SAVEMAX; i++) - m_dUSaveTime[i] = 0; - - m_dDurationUnitsMult = 0; - m_dNorthWestXExtCRS = 0; - m_dNorthWestYExtCRS = 0; - m_dSouthEastXExtCRS = 0; - m_dSouthEastYExtCRS = 0; - m_dExtCRSGridArea = 0; - m_dCellSide = 0; - m_dCellDiagonal = 0; - m_dInvCellSide = 0; - m_dInvCellDiagonal = 0; - m_dCellArea = 0; - m_dSimDuration = 0; - m_dTimeStep = 0; - m_dSimElapsed = 0; - m_dRegularSaveTime = 0; - m_dRegularSaveInterval = 0; - m_dClkLast = 0; - m_dCPUClock = 0; - m_dSeaWaterDensity = 0; - m_dThisIterSWL = 0; - m_dThisIterMeanSWL = 0; - m_dInitialMeanSWL = 0; - m_dFinalMeanSWL = 0; - m_dDeltaSWLPerTimestep = 0; - m_dBreakingWaveHeight = 0; - m_dC_0 = 0; - m_dL_0 = 0; - m_dWaveDepthRatioForWaveCalcs = 0; - m_dAllCellsDeepWaterWaveHeight = 0; - m_dAllCellsDeepWaterWaveAngle = 0; - m_dAllCellsDeepWaterWavePeriod = 0; - m_dMaxUserInputWaveHeight = 0; - m_dMaxUserInputWavePeriod = 0; - m_dR = 0; - m_dD50Fine = 0; - m_dD50Sand = 0; - m_dD50Coarse = 0; - m_dBeachSedimentDensity = 0; - m_dBeachSedimentPorosity = 0; - m_dFineErodibility = 0; - m_dSandErodibility = 0; - m_dCoarseErodibility = 0; - m_dFineErodibilityNormalized = 0; - m_dSandErodibilityNormalized = 0; - m_dCoarseErodibilityNormalized = 0; - m_dKLS = 0; - m_dKamphuis = 0; - m_dG = 0; - m_dInmersedToBulkVolumetric = 0; - m_dDepthOfClosure = 0; - m_dCoastNormalSpacing = 0; - m_dCoastNormalInterventionSpacing = 0; - m_dCoastNormalLength = 0; - m_dThisIterTotSeaDepth = 0; - m_dThisIterPotentialSedLostBeachErosion = 0; - m_dThisIterLeftGridUnconsFine = 0; // TODO067 - m_dThisIterLeftGridUnconsSand = 0; - m_dThisIterLeftGridUnconsCoarse = 0; - m_dThisIterPotentialPlatformErosion = 0; - m_dThisIterActualPlatformErosionFineCons = 0; - m_dThisIterActualPlatformErosionSandCons = 0; - m_dThisIterActualPlatformErosionCoarseCons = 0; - m_dThisIterPotentialBeachErosion = 0; - m_dThisIterBeachErosionFine = 0; - m_dThisIterBeachErosionSand = 0; - m_dThisIterBeachErosionCoarse = 0; - m_dThisIterBeachDepositionSand = 0; - m_dThisIterBeachDepositionCoarse = 0; - m_dThisIterFineSedimentToSuspension = 0; - m_dDepositionSandDiff = 0; - m_dDepositionCoarseDiff = 0; - m_dDepthOverDBMax = 0; - m_dTotPotentialPlatformErosionOnProfiles = 0; - m_dTotPotentialPlatformErosionBetweenProfiles = 0; - m_dProfileMaxSlope = 0; - m_dMaxBeachElevAboveSWL = 0; - m_dCliffErosionResistance = 0; - m_dNotchIncisionAtCollapse = 0; - m_dThisIterNewNotchApexElev = 0; - m_dNotchApexAboveMHW = 0; - m_dCliffDepositionA = 0; - m_dCliffDepositionPlanviewWidth = 0; - m_dCliffTalusMinDepositionLength = 0; - m_dMinCliffTalusHeightFrac = 0; - m_dThisIterCliffCollapseErosionFineUncons = 0; - m_dThisIterCliffCollapseErosionSandUncons = 0; - m_dThisIterCliffCollapseErosionCoarseUncons = 0; - m_dThisIterCliffCollapseErosionFineCons = 0; - m_dThisIterCliffCollapseErosionSandCons = 0; - m_dThisIterCliffCollapseErosionCoarseCons = 0; - m_dThisIterUnconsSandCliffDeposition = 0; - m_dThisIterUnconsCoarseCliffDeposition = 0; - m_dThisIterCliffCollapseFineErodedDuringDeposition = 0; - m_dThisIterCliffCollapseSandErodedDuringDeposition = 0; - m_dThisIterCliffCollapseCoarseErodedDuringDeposition = 0; - m_dCoastNormalRandSpacingFactor = 0; - m_dDeanProfileStartAboveSWL = 0; - m_dAccumulatedSeaLevelChange = 0; - m_dBreakingWaveHeightDepthRatio = 0; - m_dWaveDataWrapHours = 0; - m_dThisIterTopElevMax = 0; - m_dThisIterTopElevMin = 0; - m_dThisiterUnconsFineInput = 0; - m_dThisiterUnconsSandInput = 0; - m_dThisiterUnconsCoarseInput = 0; - m_dStartIterSuspFineAllCells = 0; - m_dStartIterSuspFineInPolygons = 0; - m_dStartIterUnconsFineAllCells = 0; - m_dStartIterUnconsSandAllCells = 0; - m_dStartIterUnconsCoarseAllCells = 0; - m_dStartIterConsFineAllCells = 0; - m_dStartIterConsSandAllCells = 0; - m_dStartIterConsCoarseAllCells = 0; - m_dThisIterDiffTotWaterLevel = 0; // Used in surge stuff TODO 007 Finish surge and runup stuff - m_dThisIterDiffWaveSetupWaterLevel = 0; - m_dThisIterDiffWaveSetupSurgeWaterLevel = 0; - m_dThisIterDiffWaveSetupSurgeRunupWaterLevel = 0; - m_dTotalFineUnconsInPolygons = 0; - m_dTotalSandUnconsInPolygons = 0; - m_dTotalCoarseUnconsInPolygons = 0; - m_dUnconsSandNotDepositedLastIter = 0; - m_dUnconsCoarseNotDepositedLastIter = 0; - m_dTotalFineConsInPolygons = 0; - m_dTotalSandConsInPolygons = 0; - m_dTotalCoarseConsInPolygons = 0; - m_dSlopeThresholdForCliffToe = 0; - m_dThisIterMHWElev = 0; - - m_dMinSWLSoFar = DBL_MAX; - m_dMaxSWLSoFar = DBL_MIN; - - for (int i = 0; i < 6; i++) - m_dGeoTransform[i] = 0; - - // TODO 011 May wish to make this a user-supplied value - m_dGISMissingValue = DBL_NODATA; - m_dMissingValue = DBL_NODATA; - - m_ldGTotPotentialPlatformErosion = 0; - m_ldGTotFineActualPlatformErosion = 0; - m_ldGTotSandActualPlatformErosion = 0; - m_ldGTotCoarseActualPlatformErosion = 0; - m_ldGTotPotentialSedLostBeachErosion = 0; - m_ldGTotActualFineLostBeachErosion = 0; - m_ldGTotActualSandLostBeachErosion = 0; - m_ldGTotActualCoarseLostBeachErosion = 0; - m_ldGTotSandSedLostCliffCollapse = 0; - m_ldGTotCoarseSedLostCliffCollapse = 0; - m_ldGTotCliffCollapseFine = 0; - m_ldGTotCliffCollapseSand = 0; - m_ldGTotCliffCollapseCoarse = 0; - m_ldGTotCliffTalusFineToSuspension = 0; - m_ldGTotCliffTalusSandDeposition = 0; - m_ldGTotCliffTalusCoarseDeposition = 0; - m_ldGTotCliffCollapseFineErodedDuringDeposition = 0; - m_ldGTotCliffCollapseSandErodedDuringDeposition = 0; - m_ldGTotCliffCollapseCoarseErodedDuringDeposition = 0; - m_ldGTotPotentialBeachErosion = 0; - m_ldGTotActualFineBeachErosion = 0; - m_ldGTotActualSandBeachErosion = 0; - m_ldGTotActualCoarseBeachErosion = 0; - m_ldGTotSandBeachDeposition = 0; - m_ldGTotCoarseBeachDeposition = 0; - m_ldGTotSuspendedSediment = 0; - m_ldGTotSandDepositionDiff = 0; - m_ldGTotCoarseDepositionDiff = 0; - m_ldGTotFineSedimentInput = 0; - m_ldGTotSandSedimentInput = 0; - m_ldGTotCoarseSedimentInput = 0; - - m_tSysStartTime = 0; - m_tSysEndTime = 0; - - m_pRasterGrid = NULL; -} - -//=============================================================================================================================== -//! The CSimulation destructor -//=============================================================================================================================== -CSimulation::~CSimulation(void) -{ - // Close output files if open - if (LogStream && LogStream.is_open()) - { - LogStream.flush(); - LogStream.close(); - } - - if (OutStream && OutStream.is_open()) - { - OutStream.flush(); - OutStream.close(); - } - - if (SeaAreaTSStream && SeaAreaTSStream.is_open()) - { - SeaAreaTSStream.flush(); - SeaAreaTSStream.close(); - } - - if (SWLTSStream && SWLTSStream.is_open()) - { - SWLTSStream.flush(); - SWLTSStream.close(); - } - - if (PlatformErosionTSStream && PlatformErosionTSStream.is_open()) - { - PlatformErosionTSStream.flush(); - PlatformErosionTSStream.close(); - } - - if (CliffCollapseErosionTSStream && CliffCollapseErosionTSStream.is_open()) - { - CliffCollapseErosionTSStream.flush(); - CliffCollapseErosionTSStream.close(); - } - - if (CliffCollapseDepositionTSStream && CliffCollapseDepositionTSStream.is_open()) - { - CliffCollapseDepositionTSStream.flush(); - CliffCollapseDepositionTSStream.close(); - } - - if (CliffCollapseNetChangeTSStream && CliffCollapseNetChangeTSStream.is_open()) - { - CliffCollapseNetChangeTSStream.flush(); - CliffCollapseNetChangeTSStream.close(); - } - - if (FineSedSuspensionTSStream && FineSedSuspensionTSStream.is_open()) - { - FineSedSuspensionTSStream.flush(); - FineSedSuspensionTSStream.close(); - } - - if (FloodSetupSurgeTSStream && FloodSetupSurgeTSStream.is_open()) - { - FloodSetupSurgeTSStream.flush(); - FloodSetupSurgeTSStream.close(); - } - - if (FloodSetupSurgeRunupTSStream && FloodSetupSurgeRunupTSStream.is_open()) - { - FloodSetupSurgeRunupTSStream.flush(); - FloodSetupSurgeRunupTSStream.close(); - } - - if (CliffNotchElevTSStream && CliffNotchElevTSStream.is_open()) - { - CliffNotchElevTSStream.flush(); - CliffNotchElevTSStream.close(); - } - - if (m_pRasterGrid) - delete m_pRasterGrid; -} - -//=============================================================================================================================== -//! The nDoSimulation member function of CSimulation sets up and runs the simulation -//=============================================================================================================================== -int CSimulation::nDoSimulation(int nArg, char const* pcArgv[]) -{ - // ================================================== initialisation section ================================================ - // Hello, World! - AnnounceStart(); - - // Start the clock ticking - StartClock(); - - // Deal with command-line parameters - int nRet = nHandleCommandLineParams(nArg, pcArgv); - - if (nRet != RTN_OK) - return (nRet); - - // Find out the folder in which the CoastalME executable sits, in order to open the .ini file (they are assumed to be in the same folder) - if (! bFindExeDir(pcArgv[0])) - return (RTN_ERR_CMEDIR); - - // OK, we are off, tell the user about the licence and the start time - AnnounceLicence(); - - // Read the .ini file and get the name of the run-data file, and path for output etc. - if (! bReadIniFile()) - return (RTN_ERR_INI); - - // Check if output dir exists - if ((! is_directory(m_strOutPath.c_str())) || (! exists(m_strOutPath.c_str()))) - { - // Output dir does not exist - bool bCreateDir = false; - - if ((isatty(fileno(stdout))) && (isatty(fileno(stderr)))) - { - // Running with stdout and stderr as a tty, so ask the user if they wish to create it - char ch; - cerr << endl - << "Output folder '" << m_strOutPath << "' does not exist. Create it? (Y/N) "; - cerr.flush(); - cin.get(ch); - - if ((ch == 'y') || (ch == 'Y')) - bCreateDir = true; - } - else - { - // Running with stdout or stderr not a tty, so create output dir rather than abort - bCreateDir = true; - } - - if (bCreateDir) - { - // Yes, so create the directory - create_directories(m_strOutPath.c_str()); - cerr << m_strOutPath << " created" << endl << endl; - } - else - // Nope, just end the run - return RTN_USER_ABORT; - } - - // We have the name of the run-data input file, so read it - if (! bReadRunDataFile()) - return RTN_ERR_RUNDATA; - - // Check raster GIS output format - if (! bCheckRasterGISOutputFormat()) - return (RTN_ERR_RASTER_GIS_OUT_FORMAT); - - // Check vector GIS output format - if (! bCheckVectorGISOutputFormat()) - return (RTN_ERR_VECTOR_GIS_OUT_FORMAT); - - // Open log file - if (! bOpenLogFile()) - return (RTN_ERR_LOGFILE); - - // Set up the time series output files - if (! bSetUpTSFiles()) - return (RTN_ERR_TSFILE); - - // Initialize the random number generators - for (int n = 0; n < NUMBER_OF_RNGS; n++) - m_Rand[n].seed(m_ulRandSeed[n]); - - // If we are doing Savitzky-Golay smoothing of the vector coastline(s), calculate the filter coefficients - if (m_nCoastSmooth == SMOOTH_SAVITZKY_GOLAY) - CalcSavitzkyGolayCoeffs(); - - // Create the raster grid object - m_pRasterGrid = new CGeomRasterGrid(this); - - // Read in the basement layer (must have this file), create the raster grid, then read in the basement DEM data to the array - AnnounceReadBasementDEM(); - nRet = nReadRasterBasementDEM(); - - if (nRet != RTN_OK) - return nRet; - - // Are we simulating cliff collapse? - if (m_bDoCliffCollapse) - { - // We are: now that we have a value for m_dCellSide, we can check some more input parameters. Talus must be more than one cell wide, and since the number of cells must be odd, three cells is the minimum width - int const nTmp = nConvertMetresToNumCells(m_dCliffDepositionPlanviewWidth); - if (nTmp < 3) - { - string const strErr = ERR + "cliff deposition must have a planview width of at least three cells. The current setting of " + to_string(m_dCliffDepositionPlanviewWidth) + " m gives a planview width of " + to_string(nTmp) + " cells. Please edit " + m_strDataPathName; - cerr << strErr << endl; - LogStream << strErr << endl; - OutStream << strErr << endl; - return RTN_ERR_RUNDATA; - } - } - - // Do some more initialisation - // cppcheck-suppress truncLongCastAssignment - m_ulNumCells = m_nXGridSize * m_nYGridSize; - - // Mark edge cells, as defined by the basement layer - nRet = nMarkBoundingBoxEdgeCells(); - if (nRet != RTN_OK) - return nRet; - - // // DEBUG CODE ================================================================================================================= - // for (int n = 0; n < m_VEdgeCell.size(); n++) - // { - // LogStream << "[" << m_VEdgeCell[n].nGetX() << "][" << m_VEdgeCell[n].nGetY() << "] = {" << dGridCentroidXToExtCRSX(m_VEdgeCell[n].nGetX()) << ", " << dGridCentroidYToExtCRSY(m_VEdgeCell[n].nGetY()) << "} " << m_VEdgeCellEdge[n] << endl; - // } - // // DEBUG CODE ================================================================================================================= - - // If we are using the default cell spacing, then now that we know the size of the raster cells, we can set the size of profile spacing in m - if (bFPIsEqual(m_dCoastNormalSpacing, 0.0, TOLERANCE)) - m_dCoastNormalSpacing = DEFAULT_PROFILE_SPACING * m_dCellSide; - else - { - // The user specified a profile spacing, is this too small? - m_nCoastNormalSpacing = nRound(m_dCoastNormalSpacing / m_dCellSide); - - if (m_nCoastNormalSpacing < DEFAULT_PROFILE_SPACING) - { - cerr << ERR << "profile spacing was specified as " << m_dCoastNormalSpacing << " m, which is " << m_nCoastNormalSpacing << " cells. Polygon creation works poorly if profile spacing is less than " << DEFAULT_PROFILE_SPACING << " cells, i.e. " << DEFAULT_PROFILE_SPACING * m_dCellSide << " m" << endl; - - LogStream << ERR << "profile spacing was specified as " << m_dCoastNormalSpacing << " m, which is " << m_nCoastNormalSpacing << " cells. Polygon creation works poorly if profile spacing is less than " << DEFAULT_PROFILE_SPACING << " cells, i.e. " << DEFAULT_PROFILE_SPACING * m_dCellSide << " m" << endl; - - return RTN_ERR_PROFILE_SPACING; - } - } - - // Set the profile spacing on interventions - m_dCoastNormalInterventionSpacing = m_dCoastNormalSpacing * INTERVENTION_PROFILE_SPACING_FACTOR; - m_nCoastNormalInterventionSpacing = nRound(m_dCoastNormalInterventionSpacing / m_dCellSide); - - // We have at least one filename for the first layer, so add the correct number of layers. Note the the number of layers does not change during the simulation: however layers can decrease in thickness until they have zero thickness - AnnounceAddLayers(); - - for (int nX = 0; nX < m_nXGridSize; nX++) - for (int nY = 0; nY < m_nYGridSize; nY++) - m_pRasterGrid->m_Cell[nX][nY].AppendLayers(m_nLayers); - - // Tell the user what is happening then read in the layer files - AnnounceReadRasterFiles(); - - for (int nLayer = 0; nLayer < m_nLayers; nLayer++) - { - if (! m_VstrInitialFineUnconsSedimentFile[nLayer].empty()) - { - // Read in the initial fine unconsolidated sediment depth file(s) - AnnounceReadInitialFineUnconsSedGIS(nLayer); - nRet = nReadRasterGISFile(FINE_UNCONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - - if (! m_VstrInitialSandUnconsSedimentFile[nLayer].empty()) - { - // Read in the initial sand unconsolidated sediment depth file - AnnounceReadInitialSandUnconsSedGIS(nLayer); - nRet = nReadRasterGISFile(SAND_UNCONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - - if (! m_VstrInitialCoarseUnconsSedimentFile[nLayer].empty()) - { - // Read in the initial coarse unconsolidated sediment depth file - AnnounceReadInitialCoarseUnconsSedGIS(nLayer); - nRet = nReadRasterGISFile(COARSE_UNCONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - - if (! m_VstrInitialFineConsSedimentFile[nLayer].empty()) - { - // Read in the initial fine consolidated sediment depth file - AnnounceReadInitialFineConsSedGIS(nLayer); - nRet = nReadRasterGISFile(FINE_CONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - - if (! m_VstrInitialSandConsSedimentFile[nLayer].empty()) - { - // Read in the initial sand consolidated sediment depth file - AnnounceReadInitialSandConsSedGIS(nLayer); - nRet = nReadRasterGISFile(SAND_CONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - - if (! m_VstrInitialCoarseConsSedimentFile[nLayer].empty()) - { - // Read in the initial coarse consolidated sediment depth file - AnnounceReadInitialCoarseConsSedGIS(nLayer); - nRet = nReadRasterGISFile(COARSE_CONS_RASTER, nLayer); - if (nRet != RTN_OK) - return (nRet); - } - } - - if (! m_strInitialSuspSedimentFile.empty()) - { - // Read in the initial suspended sediment depth file - AnnounceReadInitialSuspSedGIS(); - nRet = nReadRasterGISFile(SUSP_SED_RASTER, 0); - if (nRet != RTN_OK) - return (nRet); - } - - - // Maybe read in the landform class data, otherwise calculate this during the first timestep using identification rules - if (! m_strInitialLandformFile.empty()) - { - AnnounceReadLGIS(); - nRet = nReadRasterGISFile(LANDFORM_RASTER, 0); - if (nRet != RTN_OK) - return (nRet); - } - - // Maybe read in intervention data - if (! m_strInterventionClassFile.empty()) - { - AnnounceReadICGIS(); - nRet = nReadRasterGISFile(INTERVENTION_CLASS_RASTER, 0); - if (nRet != RTN_OK) - return (nRet); - - AnnounceReadIHGIS(); - nRet = nReadRasterGISFile(INTERVENTION_HEIGHT_RASTER, 0); - if (nRet != RTN_OK) - return (nRet); - } - - // Maybe read in the tide data - if (! m_strTideDataFile.empty()) - { - AnnounceReadTideData(); - nRet = nReadTideDataFile(); - if (nRet != RTN_OK) - return (nRet); - } - - // Read in the erosion potential shape function data - AnnounceReadSCAPEShapeFunctionFile(); - nRet = nReadShapeFunctionFile(); - if (nRet != RTN_OK) - return (nRet); - - // Do we want to output the erosion potential look-up values, for checking purposes? - if (m_bOutputErosionPotentialData) - WriteLookUpData(); - - // OK, now read in the vector files (if any) - if (m_bHaveWaveStationData || m_bSedimentInput) - AnnounceReadVectorFiles(); - - // Maybe read in deep water wave station data - if (m_bHaveWaveStationData) - { - // We are reading deep water wave height, orientation and period from a file of vector points and file time series - AnnounceReadDeepWaterWaveValuesGIS(); - - // Read in vector points - nRet = nReadVectorGISFile(DEEP_WATER_WAVE_STATIONS_VEC); - if (nRet != RTN_OK) - return (nRet); - - int const nWaveStations = static_cast(m_VnDeepWaterWaveStationID.size()); - - if (nWaveStations == 1) - m_bSingleDeepWaterWaveValues = true; - - // Read in time series values, and initialise the vector which stores each timestep's deep water wave height, orientation and period - nRet = nReadWaveStationInputFile(nWaveStations); - if (nRet != RTN_OK) - return (nRet); - } - - // Maybe read in sediment input event data - if (m_bSedimentInput) - { - // We are reading sediment input event data - AnnounceReadSedimentEventInputValuesGIS(); - - // Read in vector points for sediment input events - nRet = nReadVectorGISFile(SEDIMENT_INPUT_EVENT_LOCATION_VEC); - if (nRet != RTN_OK) - return (nRet); - - // Read in the time series values for sediment input events - nRet = nReadSedimentInputEventFile(); - if (nRet != RTN_OK) - return (nRet); - } - - // Maybe read in flood input location - if (m_bFloodLocationSave) - { - // We are reading sediment input event data - AnnounceReadFloodLocationGIS(); - - // Read in vector points for sediment input events - nRet = nReadVectorGISFile(FLOOD_LOCATION_VEC); - if (nRet != RTN_OK) - return (nRet); - } - - // Open the main output - OutStream.open(m_strOutFile.c_str(), ios::out | ios::trunc); - - if (!OutStream) - { - // Error, cannot open Out file - cerr << ERR << "cannot open " << m_strOutFile << " for output" << endl; - return (RTN_ERR_OUTFILE); - } - - // Write beginning-of-run information to Out and Log files - WriteStartRunDetails(); - - // Final stage of initialization - AnnounceFinalInitialization(); - - // Misc initialisation calcs - m_nCoastMax = COAST_LENGTH_MAX * tMax(m_nXGridSize, m_nYGridSize); // Arbitrary but probably OK - // m_nCoastMin = tMin(m_nXGridSize, m_nYGridSize); - m_nCoastMin = nRound(COAST_LENGTH_MIN_X_PROF_SPACE * m_dCoastNormalSpacing / m_dCellSide); // Arbitrary but probably OK - m_nCoastCurvatureInterval = tMax(nRound(m_dCoastNormalSpacing / (m_dCellSide * 2)), 2); // Arbitrary but probably OK - - // For beach erosion/deposition, conversion from immersed weight to bulk volumetric (sand and voids) transport rate (Leo Van Rijn) TODO 007 need full reference - m_dInmersedToBulkVolumetric = 1 / ((m_dBeachSedimentDensity - m_dSeaWaterDensity) * (1 - m_dBeachSedimentPorosity) * m_dG); - - m_bConsChangedThisIter.resize(m_nLayers, false); - m_bUnconsChangedThisIter.resize(m_nLayers, false); - - // Normalize sediment erodibility values, so that none are > 1 - double const dTmp = m_dFineErodibility + m_dSandErodibility + m_dCoarseErodibility; - m_dFineErodibilityNormalized = m_dFineErodibility / dTmp; - m_dSandErodibilityNormalized = m_dSandErodibility / dTmp; - m_dCoarseErodibilityNormalized = m_dCoarseErodibility / dTmp; - - // Intialise SWL - m_dThisIterSWL = m_dInitialMeanSWL; - - // If SWL changes during the simulation, calculate the per-timestep increment (could be -ve) - if (! bFPIsEqual(m_dFinalMeanSWL, m_dInitialMeanSWL, TOLERANCE)) - { - m_dDeltaSWLPerTimestep = (m_dTimeStep * (m_dFinalMeanSWL - m_dInitialMeanSWL)) / m_dSimDuration; - m_dAccumulatedSeaLevelChange -= m_dDeltaSWLPerTimestep; - } - - // ===================================================== The main loop ====================================================== - // Tell the user what is happening - AnnounceIsRunning(); - - while (true) - { - // Check that we haven't gone on too long: if not then update timestep number etc. - if (bTimeToQuit()) - break; - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - LogStream << "TIMESTEP " << m_ulIter << " " << string(154, '=') << endl; - - LogStream << fixed << setprecision(3); - - // Check to see if there is a new intervention in place: if so, update it on the RasterGrid array - nRet = nUpdateIntervention(); - if (nRet != RTN_OK) - return nRet; - - // Calculate changes due to external forcing (change in still water level, tide level and deep water waves height, orientation and period) - nRet = nCalcExternalForcing(); - if (nRet != RTN_OK) - return nRet; - - // Do per-timestep initialisation: set up the grid cells ready for this timestep, also initialise per-timestep totals. Note that in the first timestep, all cells (including hinterland cells) are given the deep water wave values - nRet = nInitGridAndCalcStillWaterLevel(); - if (nRet != RTN_OK) - return nRet; - - // Next find out which cells are inundated and locate the coastline(s). This also gives to all sea cells, wave values which are the same as the deep water values. For shallow water sea cells, these wave values will be changed later, in nDoAllPropagateWaves() - nRet = nLocateSeaAndCoasts(); - if (nRet != RTN_OK) - return nRet; - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // Locate estuaries TODO someday... - - if (m_bHaveConsolidatedSediment && m_bDoCliffCollapse && m_bCliffToeLocate) - { - // Locate and trace cliff toe - nRet = nLocateCliffToe(); - if (nRet != RTN_OK) - return nRet; - } - - // For all cells, use classification rules to assign sea and hinterland landform categories - nRet = nAssignLandformsForAllCells(); - if (nRet != RTN_OK) - return nRet; - - // For every coastline, use classification rules to assign landform categories - nRet = nAssignLandformsForAllCoasts(); - if (nRet != RTN_OK) - return nRet; - - // Create all coastline-normal profiles, in coastline-concave-curvature sequence - nRet = nCreateAllProfiles(); - if (nRet != RTN_OK) - return nRet; - - // Check the coastline-normal profiles for intersection, modify the profiles if they intersect, then mark valid profiles on the raster grid - nRet = nCheckAndMarkAllProfiles(); - if (nRet != RTN_OK) - return nRet; - - if (m_VCoast.size() > 1) - { - // We have multiple coastlines - nRet = nDoMultipleCoastlines(); - if (nRet != RTN_OK) - return nRet; - } - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // Create the coast polygons - nRet = nCreateAllPolygons(); - if (nRet != RTN_OK) - return nRet; - - // Mark cells of the raster grid that are within each polygon, and do some polygon initialisation - MarkPolygonCells(); - - // // DEBUG CODE ================ - // m_nGISSave++; - // if (! bWriteVectorGISFile(VECTOR_PLOT_COAST, &VECTOR_PLOT_COAST_TITLE)) - // return false; - // if (! bWriteVectorGISFile(VECTOR_PLOT_NORMALS, &VECTOR_PLOT_NORMALS_TITLE)) - // return false; - // if (! bWriteVectorGISFile(VECTOR_PLOT_INVALID_NORMALS, &VECTOR_PLOT_INVALID_NORMALS_TITLE)) - // return false; - // if (! bWriteRasterGISFile(RASTER_PLOT_NORMAL_PROFILE, &RASTER_PLOT_NORMAL_PROFILE_TITLE)) - // return false; - // if (! bWriteRasterGISFile(RASTER_PLOT_COAST, &RASTER_PLOT_COAST_TITLE)) - // return false; - // if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON, &RASTER_PLOT_POLYGON_TITLE)) - // return false; - // if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_BOUNDARY, &VECTOR_PLOT_POLYGON_BOUNDARY_TITLE)) - // return false; - // // DEBUG CODE ================ - - // Calculate the length of the shared normal between each polygon and the adjacent polygon(s) - nRet = nDoPolygonSharedBoundaries(); - if (nRet != RTN_OK) - return nRet; - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // // DEBUG CODE ========================================================================================================= - // nNODATA = 0; - // nPoly0 = 0; - // nPoly24 = 0; - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // int nTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); - // if (nTmp == INT_NODATA) - // nNODATA++; - // - // if (nTmp == 0) - // nPoly0++; - // - // if (nTmp == 24) - // nPoly24++; - // } - // } - // LogStream << "After marking polygon cells, N cells with NODATA polygon ID = " << nNODATA << endl; - // // LogStream << "After marking polygon cells, N cells with zero polygon ID = " << nPoly0 << endl; - // LogStream << "After marking polygon cells, N cells with 24 polygon ID = " << nPoly24 << endl; - // // DEBUG CODE ========================================================================================================= - // PropagateWind(); - - // Give every coast point a value for deep water wave height and direction - nRet = nSetAllCoastpointDeepWaterWaveValues(); - if (nRet != RTN_OK) - return nRet; - - // // DEBUG CODE =============== - // for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) - // { - // LogStream << "====================" << endl; - // - // for (int nProfile = 0; nProfile < m_VCoast[nCoast].nGetNumProfiles(); nProfile++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); - // int nCell = pProfile->nGetNumCellsInProfile(); - // LogStream << "Profile " << pProfile->nGetProfileID() << " nGetNumCellsInProfile() = " << nCell << endl; - // } - // - // LogStream << endl; - // - // for (int nProfile = 0; nProfile < m_VCoast[nCoast].nGetNumProfiles(); nProfile++) - // { - // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(nProfile); - // int nCell = pProfile->nGetNumCellsInProfile(); - // LogStream << "Profile " << pProfile->nGetProfileID() << " nGetNumCellsInProfile() = " << nCell << endl; - // } - // - // LogStream << "====================" << endl; - // } - // // DEBUG CODE ===================== - - // Change the wave properties in all shallow water sea cells: propagate waves and define the active zone, also locate wave shadow zones - nRet = nDoAllPropagateWaves(); - if (nRet != RTN_OK) - return nRet; - - // Output polygon share table and pre-existing sediment table to log file - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - { - WritePolygonInfoTable(); - WritePolygonPreExistingSedimentTable(); - } - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // // DEBUG CODE =========================================================================================================== - // string strOutFile = m_strOutPath; - // strOutFile += "sea_wave_height_CHECKPOINT_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // - // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); - // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // - // int nn = 0; - // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); - // } - // } - // - // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_dMissingValue); - // int nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // // DEBUG CODE =========================================================================================================== - // - // // DEBUG CODE =========================================================================================================== - // strOutFile = m_strOutPath; - // strOutFile += "sea_wave_angle_CHECKPOINT_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // - // pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); - // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // - // nn = 0; - // pdRaster = new double[m_nXGridSize * m_nYGridSize]; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); - // } - // } - // - // pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_dMissingValue); - // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // // DEBUG CODE =========================================================================================================== - - // Save the not-deposited values, to be shown in the logfile after we've finished beach sediment movement - m_dUnconsSandNotDepositedLastIter = m_dDepositionSandDiff; - m_dUnconsCoarseNotDepositedLastIter = m_dDepositionCoarseDiff; - - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - if (m_dDepositionSandDiff > MASS_BALANCE_TOLERANCE) - { - LogStream << m_ulIter << ": AT ITERATION START m_dDepositionSandDiff = " << m_dDepositionSandDiff * m_dCellArea << " m_dUnconsSandNotDepositedLastIter = " << m_dUnconsSandNotDepositedLastIter << endl; - LogStream << m_ulIter << ": AT ITERATION START m_dDepositionCoarseDiff = " << m_dDepositionCoarseDiff * m_dCellArea << " m_dUnconsCoarseNotDepositedLastIter = " << m_dUnconsCoarseNotDepositedLastIter << endl; - } - - if (m_bDoShorePlatformErosion) - { - // Calculate elevation change on the consolidated sediment which comprises the coastal platform - nRet = nDoAllShorePlatFormErosion(); - if (nRet != RTN_OK) - return nRet; - } - - // Output shore platform erosion table to log file - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - WritePolygonShorePlatformErosion(); - - // Are we considering cliff collapse? - if (m_bHaveConsolidatedSediment && m_bDoCliffCollapse) - { - // Distribute wave energy to coast landforms (currently, this is only relevant to cliffs), maybe incise cliff notches, maybe do cliff collapses - nRet = nDoAllWaveEnergyToCoastLandforms(); - if (nRet != RTN_OK) - return nRet; - - // Output cliff collapse table to log file - if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) - WritePolygonCliffCollapseErosion(); - - // Move some cliff collapse talus to unconsolidated sediment - nRet = nMoveCliffTalusToUnconsolidated(); - if (nRet != RTN_OK) - return nRet; - } - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - if (m_bDoBeachSedimentTransport) - { - // Next simulate beach erosion and deposition i.e. simulate alongshore transport of unconsolidated sediment (longshore drift) between polygons. First calculate potential sediment movement between polygons - DoAllPotentialBeachErosion(); - - // Do within-sediment redistribution of unconsolidated sediment, constraining potential sediment movement to give actual (i.e. supply-limited) sediment movement to/from each polygon in three size classes - nRet = nDoAllActualBeachErosionAndDeposition(); - if (nRet != RTN_OK) - return nRet; - } - - // If we have sediment input events, then check to see whether this is time for an event to occur. If it is, then do it - if (m_bSedimentInput) - { - nRet = nCheckForSedimentInputEvent(); - if (nRet != RTN_OK) - return nRet; - - // If we have had at least one sediment input event this iteration, then output the sediment event per polygon table to the log file - if (m_bSedimentInputThisIter && (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL)) - WritePolygonSedimentInputEventTable(); - } - - // // Add the fine sediment that was eroded this timestep (from the shore platform, from cliff collapse, from erosion of existing fine sediment during cliff collapse talus deposition, and from beach erosion; minus the fine sediment from beach erosion that went off-grid) to the suspended sediment load - // double dFineThisIter = m_dThisIterActualPlatformErosionFineCons + m_dThisIterCliffCollapseErosionFineUncons + m_dThisIterCliffCollapseErosionFineCons + m_dThisIterCliffCollapseFineErodedDuringDeposition + m_dThisIterBeachErosionFine - m_dThisIterLeftGridUnconsFine; - // - // m_dThisIterFineSedimentToSuspension += dFineThisIter; - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // // DEBUG CODE =========================================================================================================== - // string strOutFile = m_strOutPath; - // strOutFile += "sea_wave_height_CHECKPOINT_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // - // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); - // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // - // int nn = 0; - // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); - // } - // } - // - // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_dMissingValue); - // int nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // // DEBUG CODE =========================================================================================================== - // - // // DEBUG CODE =========================================================================================================== - // strOutFile = m_strOutPath; - // strOutFile += "sea_wave_angle_CHECKPOINT_"; - // strOutFile += to_string(m_ulIter); - // strOutFile += ".tif"; - // - // pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); - // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); - // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); - // pDataSet->SetGeoTransform(m_dGeoTransform); - // - // nn = 0; - // pdRaster = new double[m_nXGridSize * m_nYGridSize]; - // for (int nY = 0; nY < m_nYGridSize; nY++) - // { - // for (int nX = 0; nX < m_nXGridSize; nX++) - // { - // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); - // } - // } - // - // pBand = pDataSet->GetRasterBand(1); - // pBand->SetNoDataValue(m_dMissingValue); - // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); - // - // if (nRet == CE_Failure) - // return RTN_ERR_GRIDCREATE; - // - // GDALClose(pDataSet); - // delete[] pdRaster; - // // DEBUG CODE =========================================================================================================== - - // Do some end-of-timestep updates to the raster grid, also update per-timestep and running totals - nRet = nUpdateGrid(); - if (nRet != RTN_OK) - return nRet; - - // Make water level inundation on grid - if (m_bFloodSWLSetupSurgeLine || m_bSetupSurgeFloodMaskSave) - { - m_nLevel = 0; - - nRet = nLocateFloodAndCoasts(); - if (nRet != RTN_OK) - return nRet; - } - - if (m_bFloodSWLSetupSurgeRunupLineSave || m_bSetupSurgeRunupFloodMaskSave) - { - // TODO 007 Finish surge and runup stuff - m_nLevel = 1; - - nRet = nLocateFloodAndCoasts(); - if (nRet != RTN_OK) - return nRet; - } - - // Now save results, first the raster and vector GIS files if required - m_bSaveGISThisIter = false; - - if ((m_bSaveRegular && (m_dSimElapsed >= m_dRegularSaveTime) && (m_dSimElapsed < m_dSimDuration)) || (! m_bSaveRegular && (m_dSimElapsed >= m_dUSaveTime[m_nThisSave]))) - { - m_bSaveGISThisIter = true; - - // Save the values from the RasterGrid array into raster GIS files - if (! bSaveAllRasterGISFiles()) - return (RTN_ERR_RASTER_FILE_WRITE); - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // Save the vector GIS files - if (! bSaveAllVectorGISFiles()) - return (RTN_ERR_VECTOR_FILE_WRITE); - - // Tell the user how the simulation is progressing - AnnounceProgress(); - } - - // Output per-timestep results to the .out file - if (! bWritePerTimestepResults()) - return (RTN_ERR_TEXT_FILE_WRITE); - - // Now output time series CSV stuff - if (! bWriteTSFiles()) - return (RTN_ERR_TIMESERIES_FILE_WRITE); - - // Tell the user how the simulation is progressing - AnnounceProgress(); - - // Update grand totals - DoEndOfTimestepTotals(); - - } // ================================================ End of main loop ====================================================== - - // =================================================== post-loop tidying ===================================================== - // Tell the user what is happening - AnnounceSimEnd(); - - // Write end-of-run information to Out, Log and time-series files - nRet = nWriteEndRunDetails(); - if (nRet != RTN_OK) - return (nRet); - - // Do end-of-run memory clearance - DoEndOfRunDeletes(); - return RTN_OK; -} +/*! + \file simulation.cpp + \brief The start-of-simulation routine + \details TODO 001 A more detailed description of this routine. + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* ============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +==============================================================================================================================*/ +#include + +#include +#include +#include + +#include + +#include +using std::fixed; + +#include +using std::cerr; +using std::cin; +using std::endl; +using std::ios; + +#include +using std::setprecision; + +#include + +#include +using std::to_string; + +#include // C++17 and later, needed for missing output directory creation +using std::filesystem::is_directory; +using std::filesystem::exists; +using std::filesystem::create_directories; + +#include + +#include "cme.h" +#include "simulation.h" +#include "raster_grid.h" +#include "coast.h" + +//=============================================================================================================================== +//! The CSimulation constructor +//=============================================================================================================================== +CSimulation::CSimulation(void) +{ + // Initialization + m_bHaveFineSediment = false; + m_bHaveSandSediment = false; + m_bHaveCoarseSediment = false; + m_bBasementElevSave = false; + m_bSedIncTalusTopSurfSave = false; + m_bTopSurfIncSeaSave = false; + m_bTalusSave = false; + m_bSliceSave = false; + m_bSeaDepthSave = false; + m_bAvgSeaDepthSave = false; + m_bWaveHeightSave = false; + m_bAvgWaveHeightSave = false; + m_bWaveAngleSave = false; + m_bAvgWaveAngleSave = false; + m_bWaveAngleAndHeightSave = false; + m_bAvgWaveAngleAndHeightSave = false; + m_bDeepWaterWaveAngleAndHeightSave = false; + m_bBeachProtectionSave = false; + m_bWaveEnergySinceCollapseSave = false; + m_bMeanWaveEnergySave = false; + m_bBreakingWaveHeightSave = false; + m_bPotentialPlatformErosionSave = false; + m_bActualPlatformErosionSave = false; + m_bTotalPotentialPlatformErosionSave = false; + m_bTotalActualPlatformErosionSave = false; + m_bPotentialBeachErosionSave = false; + m_bActualBeachErosionSave = false; + m_bTotalPotentialBeachErosionSave = false; + m_bTotalActualBeachErosionSave = false; + m_bBeachDepositionSave = false; + m_bTotalBeachDepositionSave = false; + m_bLandformSave = false; + m_bSlopeConsSedSave = false; + m_bSlopeSaveForCliffToe = false; + m_bInterventionClassSave = false; + m_bInterventionHeightSave = false; + m_bSuspSedSave = false; + m_bAvgSuspSedSave = false; + m_bFineUnconsSedSave = false; + m_bSandUnconsSedSave = false; + m_bCoarseUnconsSedSave = false; + m_bFineConsSedSave = false; + m_bSandConsSedSave = false; + m_bCoarseConsSedSave = false; + m_bRasterCoastlineSave = false; + m_bRasterNormalProfileSave = false; + m_bActiveZoneSave = false; + m_bCliffCollapseSave = false; + m_bTotCliffCollapseSave = false; + m_bCliffCollapseDepositionSave = false; + m_bTotCliffCollapseDepositionSave = false; + m_bCliffNotchAllSave = false; + m_bCliffCollapseTimestepSave = false; + m_bRasterPolygonSave = false; + m_bPotentialPlatformErosionMaskSave = false; + m_bSeaMaskSave = false; + m_bBeachMaskSave = false; + m_bShadowZoneCodesSave = false; + m_bSaveRegular = false; + m_bCoastSave = false; + m_bCliffEdgeSave = false; + m_bNormalsSave = false; + m_bInvalidNormalsSave = false; + m_bCoastCurvatureSave = false; + m_bPolygonNodeSave = false; + m_bPolygonBoundarySave = false; + m_bCliffNotchSave = false; + m_bShadowBoundarySave = false; + m_bShadowDowndriftBoundarySave = false; + m_bDeepWaterWaveAngleSave = false; + m_bDeepWaterWaveHeightSave = false; + m_bDeepWaterWavePeriodSave = false; + m_bPolygonUnconsSedUpOrDownDriftSave = false; + m_bPolygonUnconsSedGainOrLossSave = false; + m_bCliffToeSave = false; + m_bSeaAreaTSSave = false; + m_bSWLTSSave = false; + m_bActualPlatformErosionTSSave = false; + m_bSuspSedTSSave = false; + m_bFloodSetupSurgeTSSave = false; + m_bFloodSetupSurgeRunupTSSave = false; + m_bCliffCollapseDepositionTSSave = false; + m_bCliffCollapseErosionTSSave = false; + m_bCliffCollapseNetTSSave = false; + m_bBeachErosionTSSave = false; + m_bBeachDepositionTSSave = false; + m_bBeachSedimentChangeNetTSSave = false; + m_bCliffNotchElevTSSave = false; + m_bSaveGISThisIter = false; + m_bOutputConsolidatedProfileData = false; + m_bOutputParallelProfileData = false; + m_bOutputErosionPotentialData = false; + m_bOmitSearchNorthEdge = false; + m_bOmitSearchSouthEdge = false; + m_bOmitSearchWestEdge = false; + m_bOmitSearchEastEdge = false; + m_bDoShorePlatformErosion = false; + m_bDoCliffCollapse = false; + m_bDoBeachSedimentTransport = false; + m_bGDALCanWriteFloat = false; + m_bGDALCanWriteInt32 = false; + m_bScaleRasterOutput = false; + m_bWorldFile = false; + m_bSingleDeepWaterWaveValues = false; + m_bHaveWaveStationData = false; + m_bSedimentInput = false; + m_bSedimentInputAtPoint = false; + m_bSedimentInputAtCoast = false; + m_bSedimentInputAlongLine = false; + m_bSedimentInputThisIter = false; + m_bSedimentInputEventSave = false; + m_bWaveSetupSave = false; + m_bStormSurgeSave = false; + m_bRiverineFlooding = false; + m_bRunUpSave = false; + m_bSetupSurgeFloodMaskSave = false; + m_bSetupSurgeRunupFloodMaskSave = false; + m_bRasterWaveFloodLineSave = false; + m_bVectorWaveFloodLineSave = false; + m_bFloodLocationSave = false; + m_bFloodSWLSetupLineSave = false; + m_bFloodSWLSetupSurgeLine = false; + m_bFloodSWLSetupSurgeRunupLineSave = false; + m_bGISSaveDigitsSequential = false; + m_bHaveConsolidatedSediment = false; + m_bGDALOptimisations = false; + m_bCliffToeLocate = false; + m_bHighestSWLSoFar = false; + m_bLowestSWLSoFar = false; + + m_bGDALCanCreate = true; + m_bCSVPerTimestepResults = true; // Default to CSV output format + m_bYamlInputFormat = false; // Default to .dat format + + m_papszGDALRasterOptions = NULL; + m_papszGDALVectorOptions = NULL; + + m_nLayers = 0; + m_nCoastSmooth = 0; + m_nCoastSmoothingWindowSize = 0; + m_nSavGolCoastPoly = 0; + m_nCliffEdgeSmooth = 0; + m_nCliffEdgeSmoothWindow = 0; + m_nSavGolCliffEdgePoly = 0; + m_nProfileSmoothWindow = 0; + m_nCoastNormalSpacing = 0; + m_nCoastNormalInterventionSpacing = 0; + m_nCoastCurvatureInterval = 0; + m_nGISMaxSaveDigits = 0; + m_nGISSave = 0; + m_nUSave = 0; + m_nThisSave = 0; + m_nXGridSize = 0; + m_nYGridSize = 0; + m_nCoastMax = 0; + m_nCoastMin = 0; + m_nNumThisIterCliffCollapse = 0; + m_nNumTotCliffCollapse = 0; + m_nUnconsSedimentHandlingAtGridEdges = 0; + m_nBeachErosionDepositionEquation = 0; + m_nWavePropagationModel = 0; + m_nSimStartSec = 0; + m_nSimStartMin = 0; + m_nSimStartHour = 0; + m_nSimStartDay = 0; + m_nSimStartMonth = 0; + m_nSimStartYear = 0; + m_nDeepWaterWaveDataNumTimeSteps = 0; + m_nLogFileDetail = 0; + m_nRunUpEquation = 0; + m_nLevel = 0; + + // TODO 011 May wish to make this a user-supplied value + m_nGISMissingValue = INT_NODATA; + m_nMissingValue = INT_NODATA; + + m_nXMinBoundingBox = INT_MAX; + m_nXMaxBoundingBox = INT_MIN; + m_nYMinBoundingBox = INT_MAX; + m_nYMaxBoundingBox = INT_MIN; + + // cppcheck-suppress useInitializationList + m_GDALWriteIntDataType = GDT_Unknown; + // cppcheck-suppress useInitializationList + m_GDALWriteFloatDataType = GDT_Unknown; + + m_lGDALMaxCanWrite = 0; + m_lGDALMinCanWrite = 0; + + m_ulIter = 0; + m_ulTotTimestep = 0; + m_ulThisIterNumPotentialBeachErosionCells = 0; + m_ulThisIterNumActualBeachErosionCells = 0; + m_ulThisIterNumBeachDepositionCells = 0; + m_ulTotPotentialPlatformErosionOnProfiles = 0; + m_ulTotPotentialPlatformErosionBetweenProfiles = 0; + m_ulMissingValueBasementCells = 0; + m_ulNumCells = 0; + m_ulThisIterNumSeaCells = 0; + m_ulThisIterNumCoastCells = 0; + m_ulThisIterNumPotentialPlatformErosionCells = 0; + m_ulThisIterNumActualPlatformErosionCells = 0; + + m_ulMissingValue = UNSIGNED_LONG_NODATA; + + for (int i = 0; i < NUMBER_OF_RNGS; i++) + m_ulRandSeed[i] = 0; + + for (int i = 0; i < SAVEMAX; i++) + m_dUSaveTime[i] = 0; + + m_dDurationUnitsMult = 0; + m_dNorthWestXExtCRS = 0; + m_dNorthWestYExtCRS = 0; + m_dSouthEastXExtCRS = 0; + m_dSouthEastYExtCRS = 0; + m_dExtCRSGridArea = 0; + m_dCellSide = 0; + m_dCellDiagonal = 0; + m_dInvCellSide = 0; + m_dInvCellDiagonal = 0; + m_dCellArea = 0; + m_dSimDuration = 0; + m_dTimeStep = 0; + m_dSimElapsed = 0; + m_dRegularSaveTime = 0; + m_dRegularSaveInterval = 0; + m_dClkLast = 0; + m_dCPUClock = 0; + m_dSeaWaterDensity = 0; + m_dThisIterSWL = 0; + m_dThisIterMeanSWL = 0; + m_dInitialMeanSWL = 0; + m_dFinalMeanSWL = 0; + m_dDeltaSWLPerTimestep = 0; + m_dBreakingWaveHeight = 0; + m_dC_0 = 0; + m_dL_0 = 0; + m_dWaveDepthRatioForWaveCalcs = 0; + m_dAllCellsDeepWaterWaveHeight = 0; + m_dAllCellsDeepWaterWaveAngle = 0; + m_dAllCellsDeepWaterWavePeriod = 0; + m_dMaxUserInputWaveHeight = 0; + m_dMaxUserInputWavePeriod = 0; + m_dR = 0; + m_dD50Fine = 0; + m_dD50Sand = 0; + m_dD50Coarse = 0; + m_dBeachSedimentDensity = 0; + m_dBeachSedimentPorosity = 0; + m_dFineErodibility = 0; + m_dSandErodibility = 0; + m_dCoarseErodibility = 0; + m_dFineErodibilityNormalized = 0; + m_dSandErodibilityNormalized = 0; + m_dCoarseErodibilityNormalized = 0; + m_dKLS = 0; + m_dKamphuis = 0; + m_dG = 0; + m_dInmersedToBulkVolumetric = 0; + m_dDepthOfClosure = 0; + m_dCoastNormalSpacing = 0; + m_dCoastNormalInterventionSpacing = 0; + m_dCoastNormalLength = 0; + m_dThisIterTotSeaDepth = 0; + m_dThisIterPotentialSedLostBeachErosion = 0; + m_dThisIterLeftGridUnconsFine = 0; // TODO067 + m_dThisIterLeftGridUnconsSand = 0; + m_dThisIterLeftGridUnconsCoarse = 0; + m_dThisIterPotentialPlatformErosion = 0; + m_dThisIterActualPlatformErosionFineCons = 0; + m_dThisIterActualPlatformErosionSandCons = 0; + m_dThisIterActualPlatformErosionCoarseCons = 0; + m_dThisIterPotentialBeachErosion = 0; + m_dThisIterBeachErosionFine = 0; + m_dThisIterBeachErosionSand = 0; + m_dThisIterBeachErosionCoarse = 0; + m_dThisIterBeachDepositionSand = 0; + m_dThisIterBeachDepositionCoarse = 0; + m_dThisIterFineSedimentToSuspension = 0; + m_dDepositionSandDiff = 0; + m_dDepositionCoarseDiff = 0; + m_dDepthOverDBMax = 0; + m_dTotPotentialPlatformErosionOnProfiles = 0; + m_dTotPotentialPlatformErosionBetweenProfiles = 0; + m_dProfileMaxSlope = 0; + m_dMaxBeachElevAboveSWL = 0; + m_dCliffErosionResistance = 0; + m_dNotchIncisionAtCollapse = 0; + m_dThisIterNewNotchApexElev = 0; + m_dNotchApexAboveMHW = 0; + m_dCliffDepositionA = 0; + m_dCliffDepositionPlanviewWidth = 0; + m_dCliffTalusMinDepositionLength = 0; + m_dMinCliffTalusHeightFrac = 0; + m_dThisIterCliffCollapseErosionFineUncons = 0; + m_dThisIterCliffCollapseErosionSandUncons = 0; + m_dThisIterCliffCollapseErosionCoarseUncons = 0; + m_dThisIterCliffCollapseErosionFineCons = 0; + m_dThisIterCliffCollapseErosionSandCons = 0; + m_dThisIterCliffCollapseErosionCoarseCons = 0; + m_dThisIterUnconsSandCliffDeposition = 0; + m_dThisIterUnconsCoarseCliffDeposition = 0; + m_dThisIterCliffCollapseFineErodedDuringDeposition = 0; + m_dThisIterCliffCollapseSandErodedDuringDeposition = 0; + m_dThisIterCliffCollapseCoarseErodedDuringDeposition = 0; + m_dCoastNormalRandSpacingFactor = 0; + m_dDeanProfileStartAboveSWL = 0; + m_dAccumulatedSeaLevelChange = 0; + m_dBreakingWaveHeightDepthRatio = 0; + m_dWaveDataWrapHours = 0; + m_dThisIterTopElevMax = 0; + m_dThisIterTopElevMin = 0; + m_dThisiterUnconsFineInput = 0; + m_dThisiterUnconsSandInput = 0; + m_dThisiterUnconsCoarseInput = 0; + m_dStartIterSuspFineAllCells = 0; + m_dStartIterSuspFineInPolygons = 0; + m_dStartIterUnconsFineAllCells = 0; + m_dStartIterUnconsSandAllCells = 0; + m_dStartIterUnconsCoarseAllCells = 0; + m_dStartIterConsFineAllCells = 0; + m_dStartIterConsSandAllCells = 0; + m_dStartIterConsCoarseAllCells = 0; + m_dThisIterDiffTotWaterLevel = 0; // Used in surge stuff TODO 007 Finish surge and runup stuff + m_dThisIterDiffWaveSetupWaterLevel = 0; + m_dThisIterDiffWaveSetupSurgeWaterLevel = 0; + m_dThisIterDiffWaveSetupSurgeRunupWaterLevel = 0; + m_dTotalFineUnconsInPolygons = 0; + m_dTotalSandUnconsInPolygons = 0; + m_dTotalCoarseUnconsInPolygons = 0; + m_dUnconsSandNotDepositedLastIter = 0; + m_dUnconsCoarseNotDepositedLastIter = 0; + m_dTotalFineConsInPolygons = 0; + m_dTotalSandConsInPolygons = 0; + m_dTotalCoarseConsInPolygons = 0; + m_dSlopeThresholdForCliffToe = 0; + m_dThisIterMHWElev = 0; + + m_dMinSWLSoFar = DBL_MAX; + m_dMaxSWLSoFar = DBL_MIN; + + for (int i = 0; i < 6; i++) + m_dGeoTransform[i] = 0; + + // TODO 011 May wish to make this a user-supplied value + m_dGISMissingValue = DBL_NODATA; + m_dMissingValue = DBL_NODATA; + + m_ldGTotPotentialPlatformErosion = 0; + m_ldGTotFineActualPlatformErosion = 0; + m_ldGTotSandActualPlatformErosion = 0; + m_ldGTotCoarseActualPlatformErosion = 0; + m_ldGTotPotentialSedLostBeachErosion = 0; + m_ldGTotActualFineLostBeachErosion = 0; + m_ldGTotActualSandLostBeachErosion = 0; + m_ldGTotActualCoarseLostBeachErosion = 0; + m_ldGTotSandSedLostCliffCollapse = 0; + m_ldGTotCoarseSedLostCliffCollapse = 0; + m_ldGTotCliffCollapseFine = 0; + m_ldGTotCliffCollapseSand = 0; + m_ldGTotCliffCollapseCoarse = 0; + m_ldGTotCliffTalusFineToSuspension = 0; + m_ldGTotCliffTalusSandDeposition = 0; + m_ldGTotCliffTalusCoarseDeposition = 0; + m_ldGTotCliffCollapseFineErodedDuringDeposition = 0; + m_ldGTotCliffCollapseSandErodedDuringDeposition = 0; + m_ldGTotCliffCollapseCoarseErodedDuringDeposition = 0; + m_ldGTotPotentialBeachErosion = 0; + m_ldGTotActualFineBeachErosion = 0; + m_ldGTotActualSandBeachErosion = 0; + m_ldGTotActualCoarseBeachErosion = 0; + m_ldGTotSandBeachDeposition = 0; + m_ldGTotCoarseBeachDeposition = 0; + m_ldGTotSuspendedSediment = 0; + m_ldGTotSandDepositionDiff = 0; + m_ldGTotCoarseDepositionDiff = 0; + m_ldGTotFineSedimentInput = 0; + m_ldGTotSandSedimentInput = 0; + m_ldGTotCoarseSedimentInput = 0; + + m_tSysStartTime = 0; + m_tSysEndTime = 0; + + m_pRasterGrid = NULL; +} + +//=============================================================================================================================== +//! The CSimulation destructor +//=============================================================================================================================== +CSimulation::~CSimulation(void) +{ + // Close output files if open + if (LogStream && LogStream.is_open()) + { + LogStream.flush(); + LogStream.close(); + } + + if (OutStream && OutStream.is_open()) + { + OutStream.flush(); + OutStream.close(); + } + + if (SeaAreaTSStream && SeaAreaTSStream.is_open()) + { + SeaAreaTSStream.flush(); + SeaAreaTSStream.close(); + } + + if (SWLTSStream && SWLTSStream.is_open()) + { + SWLTSStream.flush(); + SWLTSStream.close(); + } + + if (PlatformErosionTSStream && PlatformErosionTSStream.is_open()) + { + PlatformErosionTSStream.flush(); + PlatformErosionTSStream.close(); + } + + if (CliffCollapseErosionTSStream && CliffCollapseErosionTSStream.is_open()) + { + CliffCollapseErosionTSStream.flush(); + CliffCollapseErosionTSStream.close(); + } + + if (CliffCollapseDepositionTSStream && CliffCollapseDepositionTSStream.is_open()) + { + CliffCollapseDepositionTSStream.flush(); + CliffCollapseDepositionTSStream.close(); + } + + if (CliffCollapseNetChangeTSStream && CliffCollapseNetChangeTSStream.is_open()) + { + CliffCollapseNetChangeTSStream.flush(); + CliffCollapseNetChangeTSStream.close(); + } + + if (FineSedSuspensionTSStream && FineSedSuspensionTSStream.is_open()) + { + FineSedSuspensionTSStream.flush(); + FineSedSuspensionTSStream.close(); + } + + if (FloodSetupSurgeTSStream && FloodSetupSurgeTSStream.is_open()) + { + FloodSetupSurgeTSStream.flush(); + FloodSetupSurgeTSStream.close(); + } + + if (FloodSetupSurgeRunupTSStream && FloodSetupSurgeRunupTSStream.is_open()) + { + FloodSetupSurgeRunupTSStream.flush(); + FloodSetupSurgeRunupTSStream.close(); + } + + if (CliffNotchElevTSStream && CliffNotchElevTSStream.is_open()) + { + CliffNotchElevTSStream.flush(); + CliffNotchElevTSStream.close(); + } + + if (m_pRasterGrid) + delete m_pRasterGrid; +} + +//=============================================================================================================================== +//! The nDoSimulation member function of CSimulation sets up and runs the simulation +//=============================================================================================================================== +int CSimulation::nDoSimulation(int nArg, char const* pcArgv[]) +{ + // ================================================== initialisation section ================================================ + // Hello, World! + AnnounceStart(); + + // Start the clock ticking + StartClock(); + + // Deal with command-line parameters + int nRet = nHandleCommandLineParams(nArg, pcArgv); + + if (nRet != RTN_OK) + return (nRet); + + // Find out the folder in which the CoastalME executable sits, in order to open the .ini file (they are assumed to be in the same folder) + if (! bFindExeDir(pcArgv[0])) + return (RTN_ERR_CMEDIR); + + // OK, we are off, tell the user about the licence and the start time + AnnounceLicence(); + + // Read the .ini file and get the name of the run-data file, and path for output etc. + if (! bReadIniFile()) + return (RTN_ERR_INI); + + // Check if output dir exists + if ((! is_directory(m_strOutPath.c_str())) || (! exists(m_strOutPath.c_str()))) + { + // Output dir does not exist + bool bCreateDir = false; + + if ((isatty(fileno(stdout))) && (isatty(fileno(stderr)))) + { + // Running with stdout and stderr as a tty, so ask the user if they wish to create it + char ch; + cerr << endl + << "Output folder '" << m_strOutPath << "' does not exist. Create it? (Y/N) "; + cerr.flush(); + cin.get(ch); + + if ((ch == 'y') || (ch == 'Y')) + bCreateDir = true; + } + else + { + // Running with stdout or stderr not a tty, so create output dir rather than abort + bCreateDir = true; + } + + if (bCreateDir) + { + // Yes, so create the directory + create_directories(m_strOutPath.c_str()); + cerr << m_strOutPath << " created" << endl << endl; + } + else + // Nope, just end the run + return RTN_USER_ABORT; + } + + // We have the name of the run-data input file, so read it + if (! bReadRunDataFile()) + return RTN_ERR_RUNDATA; + + // Check raster GIS output format + if (! bCheckRasterGISOutputFormat()) + return (RTN_ERR_RASTER_GIS_OUT_FORMAT); + + // Check vector GIS output format + if (! bCheckVectorGISOutputFormat()) + return (RTN_ERR_VECTOR_GIS_OUT_FORMAT); + + // Open log file + if (! bOpenLogFile()) + return (RTN_ERR_LOGFILE); + + // Set up the time series output files + if (! bSetUpTSFiles()) + return (RTN_ERR_TSFILE); + + // Initialize the random number generators + for (int n = 0; n < NUMBER_OF_RNGS; n++) + m_Rand[n].seed(m_ulRandSeed[n]); + + // If we are doing Savitzky-Golay smoothing of the vector coastline(s), calculate the filter coefficients + if (m_nCoastSmooth == SMOOTH_SAVITZKY_GOLAY) + CalcSavitzkyGolayCoeffs(); + + // Create the raster grid object + m_pRasterGrid = new CGeomRasterGrid(this); + + // Read in the basement layer (must have this file), create the raster grid, then read in the basement DEM data to the array + AnnounceReadBasementDEM(); + nRet = nReadRasterBasementDEM(); + + if (nRet != RTN_OK) + return nRet; + + // Are we simulating cliff collapse? + if (m_bDoCliffCollapse) + { + // We are: now that we have a value for m_dCellSide, we can check some more input parameters. Talus must be more than one cell wide, and since the number of cells must be odd, three cells is the minimum width + int const nTmp = nConvertMetresToNumCells(m_dCliffDepositionPlanviewWidth); + if (nTmp < 3) + { + string const strErr = ERR + "cliff deposition must have a planview width of at least three cells. The current setting of " + to_string(m_dCliffDepositionPlanviewWidth) + " m gives a planview width of " + to_string(nTmp) + " cells. Please edit " + m_strDataPathName; + cerr << strErr << endl; + LogStream << strErr << endl; + OutStream << strErr << endl; + return RTN_ERR_RUNDATA; + } + } + + // Do some more initialisation + // cppcheck-suppress truncLongCastAssignment + m_ulNumCells = m_nXGridSize * m_nYGridSize; + + // Mark edge cells, as defined by the basement layer + nRet = nMarkBoundingBoxEdgeCells(); + if (nRet != RTN_OK) + return nRet; + + // // DEBUG CODE ================================================================================================================= + // for (int n = 0; n < m_VEdgeCell.size(); n++) + // { + // LogStream << "[" << m_VEdgeCell[n].nGetX() << "][" << m_VEdgeCell[n].nGetY() << "] = {" << dGridCentroidXToExtCRSX(m_VEdgeCell[n].nGetX()) << ", " << dGridCentroidYToExtCRSY(m_VEdgeCell[n].nGetY()) << "} " << m_VEdgeCellEdge[n] << endl; + // } + // // DEBUG CODE ================================================================================================================= + + // If we are using the default cell spacing, then now that we know the size of the raster cells, we can set the size of profile spacing in m + if (bFPIsEqual(m_dCoastNormalSpacing, 0.0, TOLERANCE)) + m_dCoastNormalSpacing = DEFAULT_PROFILE_SPACING * m_dCellSide; + else + { + // The user specified a profile spacing, is this too small? + m_nCoastNormalSpacing = nRound(m_dCoastNormalSpacing / m_dCellSide); + + if (m_nCoastNormalSpacing < DEFAULT_PROFILE_SPACING) + { + cerr << ERR << "profile spacing was specified as " << m_dCoastNormalSpacing << " m, which is " << m_nCoastNormalSpacing << " cells. Polygon creation works poorly if profile spacing is less than " << DEFAULT_PROFILE_SPACING << " cells, i.e. " << DEFAULT_PROFILE_SPACING * m_dCellSide << " m" << endl; + + LogStream << ERR << "profile spacing was specified as " << m_dCoastNormalSpacing << " m, which is " << m_nCoastNormalSpacing << " cells. Polygon creation works poorly if profile spacing is less than " << DEFAULT_PROFILE_SPACING << " cells, i.e. " << DEFAULT_PROFILE_SPACING * m_dCellSide << " m" << endl; + + return RTN_ERR_PROFILE_SPACING; + } + } + + // Set the profile spacing on interventions + m_dCoastNormalInterventionSpacing = m_dCoastNormalSpacing * INTERVENTION_PROFILE_SPACING_FACTOR; + m_nCoastNormalInterventionSpacing = nRound(m_dCoastNormalInterventionSpacing / m_dCellSide); + + // We have at least one filename for the first layer, so add the correct number of layers. Note the the number of layers does not change during the simulation: however layers can decrease in thickness until they have zero thickness + AnnounceAddLayers(); + + for (int nX = 0; nX < m_nXGridSize; nX++) + for (int nY = 0; nY < m_nYGridSize; nY++) + m_pRasterGrid->m_Cell[nX][nY].AppendLayers(m_nLayers); + + // Tell the user what is happening then read in the layer files + AnnounceReadRasterFiles(); + + for (int nLayer = 0; nLayer < m_nLayers; nLayer++) + { + if (! m_VstrInitialFineUnconsSedimentFile[nLayer].empty()) + { + // Read in the initial fine unconsolidated sediment depth file(s) + AnnounceReadInitialFineUnconsSedGIS(nLayer); + nRet = nReadRasterGISFile(FINE_UNCONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + + if (! m_VstrInitialSandUnconsSedimentFile[nLayer].empty()) + { + // Read in the initial sand unconsolidated sediment depth file + AnnounceReadInitialSandUnconsSedGIS(nLayer); + nRet = nReadRasterGISFile(SAND_UNCONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + + if (! m_VstrInitialCoarseUnconsSedimentFile[nLayer].empty()) + { + // Read in the initial coarse unconsolidated sediment depth file + AnnounceReadInitialCoarseUnconsSedGIS(nLayer); + nRet = nReadRasterGISFile(COARSE_UNCONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + + if (! m_VstrInitialFineConsSedimentFile[nLayer].empty()) + { + // Read in the initial fine consolidated sediment depth file + AnnounceReadInitialFineConsSedGIS(nLayer); + nRet = nReadRasterGISFile(FINE_CONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + + if (! m_VstrInitialSandConsSedimentFile[nLayer].empty()) + { + // Read in the initial sand consolidated sediment depth file + AnnounceReadInitialSandConsSedGIS(nLayer); + nRet = nReadRasterGISFile(SAND_CONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + + if (! m_VstrInitialCoarseConsSedimentFile[nLayer].empty()) + { + // Read in the initial coarse consolidated sediment depth file + AnnounceReadInitialCoarseConsSedGIS(nLayer); + nRet = nReadRasterGISFile(COARSE_CONS_RASTER, nLayer); + if (nRet != RTN_OK) + return (nRet); + } + } + + if (! m_strInitialSuspSedimentFile.empty()) + { + // Read in the initial suspended sediment depth file + AnnounceReadInitialSuspSedGIS(); + nRet = nReadRasterGISFile(SUSP_SED_RASTER, 0); + if (nRet != RTN_OK) + return (nRet); + } + + + // Maybe read in the landform class data, otherwise calculate this during the first timestep using identification rules + if (! m_strInitialLandformFile.empty()) + { + AnnounceReadLGIS(); + nRet = nReadRasterGISFile(LANDFORM_RASTER, 0); + if (nRet != RTN_OK) + return (nRet); + } + + // Maybe read in intervention data + if (! m_strInterventionClassFile.empty()) + { + AnnounceReadICGIS(); + nRet = nReadRasterGISFile(INTERVENTION_CLASS_RASTER, 0); + if (nRet != RTN_OK) + return (nRet); + + AnnounceReadIHGIS(); + nRet = nReadRasterGISFile(INTERVENTION_HEIGHT_RASTER, 0); + if (nRet != RTN_OK) + return (nRet); + } + + // Maybe read in the tide data + if (! m_strTideDataFile.empty()) + { + AnnounceReadTideData(); + nRet = nReadTideDataFile(); + if (nRet != RTN_OK) + return (nRet); + } + + // Read in the erosion potential shape function data + AnnounceReadSCAPEShapeFunctionFile(); + nRet = nReadShapeFunctionFile(); + if (nRet != RTN_OK) + return (nRet); + + // Do we want to output the erosion potential look-up values, for checking purposes? + if (m_bOutputErosionPotentialData) + WriteLookUpData(); + + // OK, now read in the vector files (if any) + if (m_bHaveWaveStationData || m_bSedimentInput) + AnnounceReadVectorFiles(); + + // Maybe read in deep water wave station data + if (m_bHaveWaveStationData) + { + // We are reading deep water wave height, orientation and period from a file of vector points and file time series + AnnounceReadDeepWaterWaveValuesGIS(); + + // Read in vector points + nRet = nReadVectorGISFile(DEEP_WATER_WAVE_STATIONS_VEC); + if (nRet != RTN_OK) + return (nRet); + + int const nWaveStations = static_cast(m_VnDeepWaterWaveStationID.size()); + + if (nWaveStations == 1) + m_bSingleDeepWaterWaveValues = true; + + // Read in time series values, and initialise the vector which stores each timestep's deep water wave height, orientation and period + nRet = nReadWaveStationInputFile(nWaveStations); + if (nRet != RTN_OK) + return (nRet); + } + + // Maybe read in sediment input event data + if (m_bSedimentInput) + { + // We are reading sediment input event data + AnnounceReadSedimentEventInputValuesGIS(); + + // Read in vector points for sediment input events + nRet = nReadVectorGISFile(SEDIMENT_INPUT_EVENT_LOCATION_VEC); + if (nRet != RTN_OK) + return (nRet); + + // Read in the time series values for sediment input events + nRet = nReadSedimentInputEventFile(); + if (nRet != RTN_OK) + return (nRet); + } + + // Maybe read in flood input location + if (m_bFloodLocationSave) + { + // We are reading sediment input event data + AnnounceReadFloodLocationGIS(); + + // Read in vector points for sediment input events + nRet = nReadVectorGISFile(FLOOD_LOCATION_VEC); + if (nRet != RTN_OK) + return (nRet); + } + + // Open the main output + OutStream.open(m_strOutFile.c_str(), ios::out | ios::trunc); + + if (!OutStream) + { + // Error, cannot open Out file + cerr << ERR << "cannot open " << m_strOutFile << " for output" << endl; + return (RTN_ERR_OUTFILE); + } + + // Write beginning-of-run information to Out and Log files + WriteStartRunDetails(); + + // Final stage of initialization + AnnounceFinalInitialization(); + + // Misc initialisation calcs + m_nCoastMax = COAST_LENGTH_MAX * tMax(m_nXGridSize, m_nYGridSize); // Arbitrary but probably OK + // m_nCoastMin = tMin(m_nXGridSize, m_nYGridSize); + m_nCoastMin = nRound(COAST_LENGTH_MIN_X_PROF_SPACE * m_dCoastNormalSpacing / m_dCellSide); // Arbitrary but probably OK + m_nCoastCurvatureInterval = tMax(nRound(m_dCoastNormalSpacing / (m_dCellSide * 2)), 2); // Arbitrary but probably OK + + // For beach erosion/deposition, conversion from immersed weight to bulk volumetric (sand and voids) transport rate (Leo Van Rijn) TODO 007 need full reference + m_dInmersedToBulkVolumetric = 1 / ((m_dBeachSedimentDensity - m_dSeaWaterDensity) * (1 - m_dBeachSedimentPorosity) * m_dG); + + m_bConsChangedThisIter.resize(m_nLayers, false); + m_bUnconsChangedThisIter.resize(m_nLayers, false); + + // Normalize sediment erodibility values, so that none are > 1 + double const dTmp = m_dFineErodibility + m_dSandErodibility + m_dCoarseErodibility; + m_dFineErodibilityNormalized = m_dFineErodibility / dTmp; + m_dSandErodibilityNormalized = m_dSandErodibility / dTmp; + m_dCoarseErodibilityNormalized = m_dCoarseErodibility / dTmp; + + // Intialise SWL + m_dThisIterSWL = m_dInitialMeanSWL; + + // If SWL changes during the simulation, calculate the per-timestep increment (could be -ve) + if (! bFPIsEqual(m_dFinalMeanSWL, m_dInitialMeanSWL, TOLERANCE)) + { + m_dDeltaSWLPerTimestep = (m_dTimeStep * (m_dFinalMeanSWL - m_dInitialMeanSWL)) / m_dSimDuration; + m_dAccumulatedSeaLevelChange -= m_dDeltaSWLPerTimestep; + } + + // ===================================================== The main loop ====================================================== + // Tell the user what is happening + AnnounceIsRunning(); + + while (true) + { + // Check that we haven't gone on too long: if not then update timestep number etc. + if (bTimeToQuit()) + break; + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + LogStream << "TIMESTEP " << m_ulIter << " " << string(154, '=') << endl; + + LogStream << fixed << setprecision(3); + + // Check to see if there is a new intervention in place: if so, update it on the RasterGrid array + nRet = nUpdateIntervention(); + if (nRet != RTN_OK) + return nRet; + + // Calculate changes due to external forcing (change in still water level, tide level and deep water waves height, orientation and period) + nRet = nCalcExternalForcing(); + if (nRet != RTN_OK) + return nRet; + + // Do per-timestep initialisation: set up the grid cells ready for this timestep, also initialise per-timestep totals. Note that in the first timestep, all cells (including hinterland cells) are given the deep water wave values + nRet = nInitGridAndCalcStillWaterLevel(); + if (nRet != RTN_OK) + return nRet; + + // Next find out which cells are inundated and locate the coastline(s). This also gives to all sea cells, wave values which are the same as the deep water values. For shallow water sea cells, these wave values will be changed later, in nDoAllPropagateWaves() + nRet = nLocateSeaAndCoasts(); + if (nRet != RTN_OK) + return nRet; + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // Locate estuaries TODO someday... + + if (m_bHaveConsolidatedSediment && m_bDoCliffCollapse && m_bCliffToeLocate) + { + // Locate and trace cliff toe + nRet = nLocateCliffToe(); + if (nRet != RTN_OK) + return nRet; + } + + // For all cells, use classification rules to assign sea and hinterland landform categories + nRet = nAssignLandformsForAllCells(); + if (nRet != RTN_OK) + return nRet; + + // For every coastline, use classification rules to assign landform categories + nRet = nAssignLandformsForAllCoasts(); + if (nRet != RTN_OK) + return nRet; + + // Create all coastline-normal profiles, in coastline-concave-curvature sequence + nRet = nCreateAllProfiles(); + if (nRet != RTN_OK) + return nRet; + + // Check the coastline-normal profiles for intersection, modify the profiles if they intersect, then mark valid profiles on the raster grid + nRet = nCheckAndMarkAllProfiles(); + if (nRet != RTN_OK) + return nRet; + + if (m_VCoast.size() > 1) + { + // We have multiple coastlines + nRet = nDoMultipleCoastlines(); + if (nRet != RTN_OK) + return nRet; + } + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // Create the coast polygons + nRet = nCreateAllPolygons(); + if (nRet != RTN_OK) + return nRet; + + // Mark cells of the raster grid that are within each polygon, and do some polygon initialisation + MarkPolygonCells(); + + // // DEBUG CODE ================ + // m_nGISSave++; + // if (! bWriteVectorGISFile(VECTOR_PLOT_COAST, &VECTOR_PLOT_COAST_TITLE)) + // return false; + // if (! bWriteVectorGISFile(VECTOR_PLOT_NORMALS, &VECTOR_PLOT_NORMALS_TITLE)) + // return false; + // if (! bWriteVectorGISFile(VECTOR_PLOT_INVALID_NORMALS, &VECTOR_PLOT_INVALID_NORMALS_TITLE)) + // return false; + // if (! bWriteRasterGISFile(RASTER_PLOT_NORMAL_PROFILE, &RASTER_PLOT_NORMAL_PROFILE_TITLE)) + // return false; + // if (! bWriteRasterGISFile(RASTER_PLOT_COAST, &RASTER_PLOT_COAST_TITLE)) + // return false; + // if (! bWriteRasterGISFile(RASTER_PLOT_POLYGON, &RASTER_PLOT_POLYGON_TITLE)) + // return false; + // if (! bWriteVectorGISFile(VECTOR_PLOT_POLYGON_BOUNDARY, &VECTOR_PLOT_POLYGON_BOUNDARY_TITLE)) + // return false; + // // DEBUG CODE ================ + + // Calculate the length of the shared normal between each polygon and the adjacent polygon(s) + nRet = nDoPolygonSharedBoundaries(); + if (nRet != RTN_OK) + return nRet; + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // // DEBUG CODE ========================================================================================================= + // nNODATA = 0; + // nPoly0 = 0; + // nPoly24 = 0; + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // int nTmp = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID(); + // if (nTmp == INT_NODATA) + // nNODATA++; + // + // if (nTmp == 0) + // nPoly0++; + // + // if (nTmp == 24) + // nPoly24++; + // } + // } + // LogStream << "After marking polygon cells, N cells with NODATA polygon ID = " << nNODATA << endl; + // // LogStream << "After marking polygon cells, N cells with zero polygon ID = " << nPoly0 << endl; + // LogStream << "After marking polygon cells, N cells with 24 polygon ID = " << nPoly24 << endl; + // // DEBUG CODE ========================================================================================================= + // PropagateWind(); + + // Give every coast point a value for deep water wave height and direction + nRet = nSetAllCoastpointDeepWaterWaveValues(); + if (nRet != RTN_OK) + return nRet; + + // // DEBUG CODE =============== + // for (int nCoast = 0; nCoast < static_cast(m_VCoast.size()); nCoast++) + // { + // LogStream << "====================" << endl; + // + // for (int nProfile = 0; nProfile < m_VCoast[nCoast].nGetNumProfiles(); nProfile++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile); + // int nCell = pProfile->nGetNumCellsInProfile(); + // LogStream << "Profile " << pProfile->nGetProfileID() << " nGetNumCellsInProfile() = " << nCell << endl; + // } + // + // LogStream << endl; + // + // for (int nProfile = 0; nProfile < m_VCoast[nCoast].nGetNumProfiles(); nProfile++) + // { + // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(nProfile); + // int nCell = pProfile->nGetNumCellsInProfile(); + // LogStream << "Profile " << pProfile->nGetProfileID() << " nGetNumCellsInProfile() = " << nCell << endl; + // } + // + // LogStream << "====================" << endl; + // } + // // DEBUG CODE ===================== + + // Change the wave properties in all shallow water sea cells: propagate waves and define the active zone, also locate wave shadow zones + nRet = nDoAllPropagateWaves(); + if (nRet != RTN_OK) + return nRet; + + // Output polygon share table and pre-existing sediment table to log file + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + { + WritePolygonInfoTable(); + WritePolygonPreExistingSedimentTable(); + } + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // // DEBUG CODE =========================================================================================================== + // string strOutFile = m_strOutPath; + // strOutFile += "sea_wave_height_CHECKPOINT_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // + // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); + // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // + // int nn = 0; + // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); + // } + // } + // + // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_dMissingValue); + // int nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // // DEBUG CODE =========================================================================================================== + // + // // DEBUG CODE =========================================================================================================== + // strOutFile = m_strOutPath; + // strOutFile += "sea_wave_angle_CHECKPOINT_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // + // pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); + // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // + // nn = 0; + // pdRaster = new double[m_nXGridSize * m_nYGridSize]; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); + // } + // } + // + // pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_dMissingValue); + // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // // DEBUG CODE =========================================================================================================== + + // Save the not-deposited values, to be shown in the logfile after we've finished beach sediment movement + m_dUnconsSandNotDepositedLastIter = m_dDepositionSandDiff; + m_dUnconsCoarseNotDepositedLastIter = m_dDepositionCoarseDiff; + + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + if (m_dDepositionSandDiff > MASS_BALANCE_TOLERANCE) + { + LogStream << m_ulIter << ": AT ITERATION START m_dDepositionSandDiff = " << m_dDepositionSandDiff * m_dCellArea << " m_dUnconsSandNotDepositedLastIter = " << m_dUnconsSandNotDepositedLastIter << endl; + LogStream << m_ulIter << ": AT ITERATION START m_dDepositionCoarseDiff = " << m_dDepositionCoarseDiff * m_dCellArea << " m_dUnconsCoarseNotDepositedLastIter = " << m_dUnconsCoarseNotDepositedLastIter << endl; + } + + if (m_bDoShorePlatformErosion) + { + // Calculate elevation change on the consolidated sediment which comprises the coastal platform + nRet = nDoAllShorePlatFormErosion(); + if (nRet != RTN_OK) + return nRet; + } + + // Output shore platform erosion table to log file + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + WritePolygonShorePlatformErosion(); + + // Are we considering cliff collapse? + if (m_bHaveConsolidatedSediment && m_bDoCliffCollapse) + { + // Distribute wave energy to coast landforms (currently, this is only relevant to cliffs), maybe incise cliff notches, maybe do cliff collapses + nRet = nDoAllWaveEnergyToCoastLandforms(); + if (nRet != RTN_OK) + return nRet; + + // Output cliff collapse table to log file + if (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL) + WritePolygonCliffCollapseErosion(); + + // Move some cliff collapse talus to unconsolidated sediment + nRet = nMoveCliffTalusToUnconsolidated(); + if (nRet != RTN_OK) + return nRet; + } + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + if (m_bDoBeachSedimentTransport) + { + // Next simulate beach erosion and deposition i.e. simulate alongshore transport of unconsolidated sediment (longshore drift) between polygons. First calculate potential sediment movement between polygons + DoAllPotentialBeachErosion(); + + // Do within-sediment redistribution of unconsolidated sediment, constraining potential sediment movement to give actual (i.e. supply-limited) sediment movement to/from each polygon in three size classes + nRet = nDoAllActualBeachErosionAndDeposition(); + if (nRet != RTN_OK) + return nRet; + } + + // If we have sediment input events, then check to see whether this is time for an event to occur. If it is, then do it + if (m_bSedimentInput) + { + nRet = nCheckForSedimentInputEvent(); + if (nRet != RTN_OK) + return nRet; + + // If we have had at least one sediment input event this iteration, then output the sediment event per polygon table to the log file + if (m_bSedimentInputThisIter && (m_nLogFileDetail >= LOG_FILE_MIDDLE_DETAIL)) + WritePolygonSedimentInputEventTable(); + } + + // // Add the fine sediment that was eroded this timestep (from the shore platform, from cliff collapse, from erosion of existing fine sediment during cliff collapse talus deposition, and from beach erosion; minus the fine sediment from beach erosion that went off-grid) to the suspended sediment load + // double dFineThisIter = m_dThisIterActualPlatformErosionFineCons + m_dThisIterCliffCollapseErosionFineUncons + m_dThisIterCliffCollapseErosionFineCons + m_dThisIterCliffCollapseFineErodedDuringDeposition + m_dThisIterBeachErosionFine - m_dThisIterLeftGridUnconsFine; + // + // m_dThisIterFineSedimentToSuspension += dFineThisIter; + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // // DEBUG CODE =========================================================================================================== + // string strOutFile = m_strOutPath; + // strOutFile += "sea_wave_height_CHECKPOINT_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // + // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); + // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // + // int nn = 0; + // double* pdRaster = new double[m_nXGridSize * m_nYGridSize]; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight(); + // } + // } + // + // GDALRasterBand* pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_dMissingValue); + // int nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // // DEBUG CODE =========================================================================================================== + // + // // DEBUG CODE =========================================================================================================== + // strOutFile = m_strOutPath; + // strOutFile += "sea_wave_angle_CHECKPOINT_"; + // strOutFile += to_string(m_ulIter); + // strOutFile += ".tif"; + // + // pDriver = GetGDALDriverManager()->GetDriverByName("gtiff"); + // pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions); + // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str()); + // pDataSet->SetGeoTransform(m_dGeoTransform); + // + // nn = 0; + // pdRaster = new double[m_nXGridSize * m_nYGridSize]; + // for (int nY = 0; nY < m_nYGridSize; nY++) + // { + // for (int nX = 0; nX < m_nXGridSize; nX++) + // { + // pdRaster[nn++] = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle(); + // } + // } + // + // pBand = pDataSet->GetRasterBand(1); + // pBand->SetNoDataValue(m_dMissingValue); + // nRet = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL); + // + // if (nRet == CE_Failure) + // return RTN_ERR_GRIDCREATE; + // + // GDALClose(pDataSet); + // delete[] pdRaster; + // // DEBUG CODE =========================================================================================================== + + // Do some end-of-timestep updates to the raster grid, also update per-timestep and running totals + nRet = nUpdateGrid(); + if (nRet != RTN_OK) + return nRet; + + // Make water level inundation on grid + if (m_bFloodSWLSetupSurgeLine || m_bSetupSurgeFloodMaskSave) + { + m_nLevel = 0; + + nRet = nLocateFloodAndCoasts(); + if (nRet != RTN_OK) + return nRet; + } + + if (m_bFloodSWLSetupSurgeRunupLineSave || m_bSetupSurgeRunupFloodMaskSave) + { + // TODO 007 Finish surge and runup stuff + m_nLevel = 1; + + nRet = nLocateFloodAndCoasts(); + if (nRet != RTN_OK) + return nRet; + } + + // Now save results, first the raster and vector GIS files if required + m_bSaveGISThisIter = false; + + if ((m_bSaveRegular && (m_dSimElapsed >= m_dRegularSaveTime) && (m_dSimElapsed < m_dSimDuration)) || (! m_bSaveRegular && (m_dSimElapsed >= m_dUSaveTime[m_nThisSave]))) + { + m_bSaveGISThisIter = true; + + // Save the values from the RasterGrid array into raster GIS files + if (! bSaveAllRasterGISFiles()) + return (RTN_ERR_RASTER_FILE_WRITE); + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // Save the vector GIS files + if (! bSaveAllVectorGISFiles()) + return (RTN_ERR_VECTOR_FILE_WRITE); + + // Tell the user how the simulation is progressing + AnnounceProgress(); + } + + // Output per-timestep results to the .out file + if (! bWritePerTimestepResults()) + return (RTN_ERR_TEXT_FILE_WRITE); + + // Now output time series CSV stuff + if (! bWriteTSFiles()) + return (RTN_ERR_TIMESERIES_FILE_WRITE); + + // Tell the user how the simulation is progressing + AnnounceProgress(); + + // Update grand totals + DoEndOfTimestepTotals(); + + } // ================================================ End of main loop ====================================================== + + // =================================================== post-loop tidying ===================================================== + // Tell the user what is happening + AnnounceSimEnd(); + + // Write end-of-run information to Out, Log and time-series files + nRet = nWriteEndRunDetails(); + if (nRet != RTN_OK) + return (nRet); + + // Do end-of-run memory clearance + DoEndOfRunDeletes(); + return RTN_OK; +} diff --git a/src/simulation.h b/src/simulation.h index 879268868..6764e7201 100644 --- a/src/simulation.h +++ b/src/simulation.h @@ -1,1934 +1,1934 @@ -/*! - \class CSimulation - \brief This class runs CoastalME simulations - \details TODO 001 This is a more detailed description of the CSimulation - class - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License - \file simulation.h - \brief Contains CSimulation definitions -*/ - -#ifndef SIMULATION_H -#define SIMULATION_H -/* =============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -===============================================================================================================================*/ -#include -using std::vector; - -#include -using std::localtime; -using std::time; -using std::time_t; - -#include -using std::ofstream; - -#include -using std::string; - -#include -using std::pair; - -#include -using std::stack; - -#include -using std::default_random_engine; -using std::normal_distribution; - -#include -using ::GDALDataType; - -#include "yaml_parser.h" -#include "configuration.h" -#include "2d_point.h" -#include "2di_point.h" -#include "line.h" -#include "cme.h" -#include "line.h" - -class CGeomRasterGrid; // Forward declarations -class CRWCoast; -class CGeomProfile; -class CGeomCoastPolygon; -class CRWCliff; -class CRWSedInputEvent; -class CRWCellLandform; - -class CSimulation -{ - private: - //! Does this simulation consider fine-sized sediment? - bool m_bHaveFineSediment; - - //! Does this simulation consider sand-sized sediment? - bool m_bHaveSandSediment; - - //! Does this simulation consider coarse-sized sediment? - bool m_bHaveCoarseSediment; - - //! Save basement raster DEMs? - bool m_bBasementElevSave; - - //! Save sediment (inc talus) top surface raster DEMs? - bool m_bSedIncTalusTopSurfSave; - - //! Save top surface (sediment, talus, and sea) raster DEMs? - bool m_bTopSurfIncSeaSave; - - //! Save talus depth? - bool m_bTalusSave; - - //! Save slope raster grids (used for cliff toe location)? - bool m_bSlopeSaveForCliffToe; - - //! Save cliff toe raster grids? - bool m_bCliffToeSave; - - //! Save slices? - bool m_bSliceSave; - - //! Save sea depth raster GIS files? - bool m_bSeaDepthSave; - - //! Save average sea depth raster GIS files? - bool m_bAvgSeaDepthSave; - - //! Save wave height raster GIS files? - bool m_bWaveHeightSave; - - //! Save wave height raster GIS files? - bool m_bAvgWaveHeightSave; - - //! Save wave angle raster GIS files? - bool m_bWaveAngleSave; - - //! Save average wave angle raster GIS files? - bool m_bAvgWaveAngleSave; - - //! Save wave angle and wave height raster GIS files? - bool m_bWaveAngleAndHeightSave; - - //! Save average wave angle and average wave height raster GIS files? - bool m_bAvgWaveAngleAndHeightSave; - - //! Save deep water wave angle and wave height raster GIS files? - bool m_bDeepWaterWaveAngleAndHeightSave; - - //! Save wave energy since cliff collapse raster GIS files? - bool m_bWaveEnergySinceCollapseSave; - - //! Save mean wave energy raster GIS files? - bool m_bMeanWaveEnergySave; - - //! Save breaking wave height raster GIS files? - bool m_bBreakingWaveHeightSave; - - //! Save beach protection raster GIS files> - bool m_bBeachProtectionSave; - - //! Save potential shore platform erosion raster GIS files? - bool m_bPotentialPlatformErosionSave; - - //! Save actual (supply-limited) shore platform erosion raster GIS files? - bool m_bActualPlatformErosionSave; - - //! Save total potential shore platform erosion raster GIS files? - bool m_bTotalPotentialPlatformErosionSave; - - //! Save total actual (supply-limited) shore platform erosion raster GIS files? - bool m_bTotalActualPlatformErosionSave; - - //! Save potential beach (unconsolidated sediment) erosion raster GIS files? - bool m_bPotentialBeachErosionSave; - - //! Save actual (supply-limited) beach (unconsolidated sediment) erosion raster GIS files? - bool m_bActualBeachErosionSave; - - //! Save total potential beach (unconsolidated sediment) erosion raster GIS files? - bool m_bTotalPotentialBeachErosionSave; - - //! Save total actual (supply-limited) beach (unconsolidated sediment) erosion raster GIS files? - bool m_bTotalActualBeachErosionSave; - - //! Save beach (unconsolidated sediment) deposition raster GIS files? - bool m_bBeachDepositionSave; - - //! Save total beach (unconsolidated sediment) deposition raster GIS files? - bool m_bTotalBeachDepositionSave; - - //! Save coast landform raster GIS files? - bool m_bLandformSave; - - //! Save slope of consolidated sediment raster GIS files? - bool m_bSlopeConsSedSave; - - //! Save intervention class raster GIS files? - bool m_bInterventionClassSave; - - //! Save intervention height raster GIS files? - bool m_bInterventionHeightSave; - - //! Save suspended sediment raster GIS files? - bool m_bSuspSedSave; - - //! Save average suspended sediment raster GIS files? - bool m_bAvgSuspSedSave; - - //! Save fine unconsolidated sediment raster GIS files? - bool m_bFineUnconsSedSave; - - //! Save sand unconsolidated sediment raster GIS files? - bool m_bSandUnconsSedSave; - - //! Save coarse unconsolidated sediment raster GIS files? - bool m_bCoarseUnconsSedSave; - - //! Save fine consolidated sediment raster GIS files? - bool m_bFineConsSedSave; - - //! Save sand consolidated sediment raster GIS files? - bool m_bSandConsSedSave; - - //! Save coarse consolidated sediment raster GIS files? - bool m_bCoarseConsSedSave; - - //! Save rasterized coastline GIS files? - bool m_bRasterCoastlineSave; - - //! Save rasterized coastline-normal profiles GIS files? - bool m_bRasterNormalProfileSave; - - //! Save active zone raster GIS files? - bool m_bActiveZoneSave; - - //! Save cliff collapse raster GIS files? - bool m_bCliffCollapseSave; - - //! Save total cliff collapse raster GIS files? - bool m_bTotCliffCollapseSave; - - //! Save cliff collapse deposition raster GIS files? - bool m_bCliffCollapseDepositionSave; - - //! Save total cliff collapse deposition raster GIS files? - bool m_bTotCliffCollapseDepositionSave; - - //! Save raster polygon raster GIS files? - bool m_bRasterPolygonSave; - - //! Save potential platform erosion mask raster GIS files? - bool m_bPotentialPlatformErosionMaskSave; - - //! Save sea mask raster GIS files? - bool m_bSeaMaskSave; - - //! Save beach mask raster GIS files? - bool m_bBeachMaskSave; - - //! Save wave shadow zones raster GIS files? - bool m_bShadowZoneCodesSave; - - //! Save deep water wave angle raster GIS files? - bool m_bDeepWaterWaveAngleSave; - - //! Save deep water wave height raster GIS files? - bool m_bDeepWaterWaveHeightSave; - - //! Save deep water wave period raster GIS files? - bool m_bDeepWaterWavePeriodSave; - - //! Save polygon unconsolidated sediment up- or down-drift raster GIS files? - bool m_bPolygonUnconsSedUpOrDownDriftSave; - - //! Save polygon unconsolidated sediment gain or loss raster GIS files? - bool m_bPolygonUnconsSedGainOrLossSave; - - //! Save GIS files at regular intervals? - bool m_bSaveRegular; - - //! Save coastline as vector GIS file? - bool m_bCoastSave; - - //! Save cliff edge vector GIS files? - bool m_bCliffEdgeSave; - - //! Save coastline-normal vector GIS files? - bool m_bNormalsSave; - - //! Save invalid coastline-normal vector GIS files? - bool m_bInvalidNormalsSave; - - //! Save coastline-curvature vector GIS files? - bool m_bCoastCurvatureSave; - - //! Save polygon node vector GIS files? - bool m_bPolygonNodeSave; - - //! Save polygon boundary vector GIS files? - bool m_bPolygonBoundarySave; - - //! Save cliff notch incision depth vector GIS files? - bool m_bCliffNotchSave; - - //! Save wave transect points vector GIS files? - bool m_bWaveTransectPointsSave; - - //! Save wave shadow boundary vector GIS files? - bool m_bShadowBoundarySave; - - //! Save wave shadow downdrift boundary vector GIS files? - bool m_bShadowDowndriftBoundarySave; - - //! Save the sea area time series file? - bool m_bSeaAreaTSSave; - - //! Save the SWL (still water level) time series file? - bool m_bSWLTSSave; - - //! Save the actual (supply-limited) shore platform erosion time series file? - bool m_bActualPlatformErosionTSSave; - - //! Save the cliff collapse erosion time series file? - bool m_bCliffCollapseErosionTSSave; - - //! Save the cliff collapse deposition time series file? - bool m_bCliffCollapseDepositionTSSave; - - //! Save the cliff collapse net change time series file? - bool m_bCliffCollapseNetTSSave; - - //! Save the beach (unconsolidated sediment) erosion time series file? - bool m_bBeachErosionTSSave; - - //! Save the beach (unconsolidated sediment) deposition time series file? - bool m_bBeachDepositionTSSave; - - //! Save the beach (unconsolidated sediment) net change time series file? - bool m_bBeachSedimentChangeNetTSSave; - - //! Save the suspended sediment time series file? - bool m_bSuspSedTSSave; - - //! Save the flood setup surge time series file? TODO 007 Finish surge and runup stuff - bool m_bFloodSetupSurgeTSSave; - - //! Save the flood setup surge runup time series file? TODO 007 Finish surge and runup stuff - bool m_bFloodSetupSurgeRunupTSSave; - - //! Save the cliff notch elevation time series file? - bool m_bCliffNotchElevTSSave; - - //! Save GIS files this iteration? - bool m_bSaveGISThisIter; - - //! Output profile data? - bool m_bOutputConsolidatedProfileData; - - //! Output parallel profile data? - bool m_bOutputParallelProfileData; - - //! Output erosion potential data? - bool m_bOutputErosionPotentialData; - - //! Output per-timestep results in CSV format instead of fixed-width? - bool m_bCSVPerTimestepResults; - - //! Use YAML format for input datafile instead of .dat format? - bool m_bYamlInputFormat; - - //! Omit the north edge of the grid from coast-end searches? - bool m_bOmitSearchNorthEdge; - - //! Omit the south edge of the grid from coast-end searches? - bool m_bOmitSearchSouthEdge; - - //! Omit the west edge of the grid from coast-end searches? - bool m_bOmitSearchWestEdge; - - //! Omit the east edge of the grid from coast-end searches? - bool m_bOmitSearchEastEdge; - - //! Simulate shore platform erosion? - bool m_bDoShorePlatformErosion; - - //! Simulate cliff collapse? - bool m_bDoCliffCollapse; - - //! Simulate unconsolidated sediment (beach) transport? - bool m_bDoBeachSedimentTransport; - - //! Is the selected GDAL output file format capable of writing files? - bool m_bGDALCanCreate; - - //! Is the selected GDAL output file format capable of writing floating-point values to files? - bool m_bGDALCanWriteFloat; - - //! Is the selected GDAL output file format capable of writing 32-bit integers to files? - bool m_bGDALCanWriteInt32; - - //! Scale raster output? - bool m_bScaleRasterOutput; - - //! Write a GIS World file? - bool m_bWorldFile; - - //! Do we have just a point source for (i.e. only a single measurement of) deep water wave values - bool m_bSingleDeepWaterWaveValues; - - //! Do we have wave station data? - bool m_bHaveWaveStationData; - - //! Do we have sediment input events? - bool m_bSedimentInput; - - //! Do we have sediment inputat a point? - bool m_bSedimentInputAtPoint; - - //! Do we have sediment input at the coast? - bool m_bSedimentInputAtCoast; - - //! Do we have sediment input along a line? - bool m_bSedimentInputAlongLine; - - //! Save sediment inut data? - bool m_bSedimentInputEventSave; - - //! Do we have a sediment input event this iteration? - bool m_bSedimentInputThisIter; - - //! Are we doing riverine flooding? - bool m_bRiverineFlooding; - - //! Are we saving the wave setup? TODO 007 Finish surge and runup stuff - bool m_bWaveSetupSave; - - //! Are we saving the storm surge? TODO 007 Finish surge and runup stuff - bool m_bStormSurgeSave; - - //! Are we saving runup? TODO 007 Finish surge and runup stuff - bool m_bRunUpSave; - - //! Are we saving the setup surge flood mask? TODO 007 Finish surge and runup stuff - bool m_bSetupSurgeFloodMaskSave; - - //! Are we saving the setup surge runup flood mask? TODO 007 Finish surge and runup stuff - bool m_bSetupSurgeRunupFloodMaskSave; - - //! Are we saving the raster wave flood line? TODO 007 Finish surge and runup stuff - bool m_bRasterWaveFloodLineSave; - - //! Are we saving the vector wave flood line? TODO 007 Finish surge and runup stuff - bool m_bVectorWaveFloodLineSave; - - //! Are we saving the flood location? TODO 007 Finish surge and runup stuff - bool m_bFloodLocationSave; - - //! Are we saving the flood still water level setup line? TODO 007 Finish surge and runup stuff - bool m_bFloodSWLSetupLineSave; - - //! Are we saving the flood still water level setup surge line? TODO 007 Finish surge and runup stuff - bool m_bFloodSWLSetupSurgeLine; - - //! Are we saving all cliff notches? - bool m_bCliffNotchAllSave; - - //! Are we saving the timestep at which each cliff occurred? - bool m_bCliffCollapseTimestepSave; - - //! Are we saving the flood still water level setup surge runup line? TODO 007 Finish surge and runup stuff - bool m_bFloodSWLSetupSurgeRunupLineSave; - - //! Are the GIS save digits (which are part of each GIS file name) sequential, or are they the iteration number? - bool m_bGISSaveDigitsSequential; - - //! Does this simulation consider consolidated sediment, or is it an unconsolidated sediment only simulation?> - bool m_bHaveConsolidatedSediment; - - //! GDAL optimisations enabled? - bool m_bGDALOptimisations; - - //! Cliff to location? - bool m_bCliffToeLocate; - - //! Do we have the highest SWL so far? - bool m_bHighestSWLSoFar; - - //! Do we have the lowest SWL so far? - bool m_bLowestSWLSoFar; - - //! Options for GDAL when handling raster files - char **m_papszGDALRasterOptions; - - //! Options for GDAL when handling vector files - char **m_papszGDALVectorOptions; - - //! The size of the grid in the x direction - int m_nXGridSize; - - //! The size of the grid in the y direction - int m_nYGridSize; - - //! The number of sediment layers - int m_nLayers; - - //! Which method to use for coast smoothing - int m_nCoastSmooth; - - //! The size of the window used for coast smoothing. Must be an odd number - int m_nCoastSmoothingWindowSize; - - //! The order of the coastline profile smoothing polynomial if Savitzky-Golay smoothing is used (usually 2 or 4, max is 6) - int m_nSavGolCoastPoly; - - //! Which method to use for cliff edge smoothing - int m_nCliffEdgeSmooth; - - //! The size of the window used for cliff edge smoothing. Must be an odd number - int m_nCliffEdgeSmoothWindow; - - //! The order of the cliff edge smoothing polynomial if Savitzky-Golay smoothing is used (usually 2 or 4, max is 6) - int m_nSavGolCliffEdgePoly; - - //! The size of the window used for running-mean coast-normal profile smoothing (must be odd) - int m_nProfileSmoothWindow; - - //! Average spacing between coastline normals, measured in cells - int m_nCoastNormalSpacing; - - //! Average spacing between coastline normals on interventions, measured in cells - int m_nCoastNormalInterventionSpacing; - - //! Coast curvature interval is a length, measured in coastline points - int m_nCoastCurvatureInterval; - - //! The maximum number of digits in GIS filenames. These can be sequential, or the iteration number - int m_nGISMaxSaveDigits; - - //! The save number for GIS files (can be sequential, or the iteration number) - int m_nGISSave; - - //! If user-defined GIS save intervals, the number of these - int m_nUSave; - - //! Used in calculations of GIS save intervals - int m_nThisSave; - - //! Maximum valid coast length when searching for coasts - int m_nCoastMax; - - //! Minimum valid coast length when searching for coasts - int m_nCoastMin; - - //! The number of cells with cliff collapse this iteration - int m_nNumThisIterCliffCollapse; - - //! The total number of cells with cliff collapse since the start of the simulation - int m_nNumTotCliffCollapse; - - //! How sediment which moves off an edge of the grid is handled. Possible values are GRID_EDGE_CLOSED, GRID_EDGE_OPEN, GRID_EDGE_RECIRCULATE - int m_nUnconsSedimentHandlingAtGridEdges; - - //! Which beach erosion-deposition equation is used. Possible values are UNCONS_SEDIMENT_EQUATION_CERC and UNCONS_SEDIMENT_EQUATION_KAMPHUIS - int m_nBeachErosionDepositionEquation; - - //! The value for integer missing values, as read from GIS input files - int m_nGISMissingValue; - - //! Used by CoastalME for integer missing values - int m_nMissingValue; - - //! The minimum x value of the bounding box - int m_nXMinBoundingBox; - - //! The maximum x value of the bounding box - int m_nXMaxBoundingBox; - - //! The minimum y value of the bounding box - int m_nYMinBoundingBox; - - //! The maximum y value of the bounding box - int m_nYMaxBoundingBox; - - //! The wave propagation model used. Possible values are WAVE_MODEL_CSHORE and WAVE_MODEL_COVE - int m_nWavePropagationModel; - - //! Start time of the simulation (seconds) - int m_nSimStartSec; - - //! Start time of the simulation (minutes) - int m_nSimStartMin; - - //! Start time of the simulation (hours) - int m_nSimStartHour; - - //! Start date of the simulation (day) - int m_nSimStartDay; - - //! Start date of the simulation (month) - int m_nSimStartMonth; - - //! Start date of the simulation (year) - int m_nSimStartYear; - - //! The duration of data for deep water waves, expressed as a number of timesteps - int m_nDeepWaterWaveDataNumTimeSteps; - - //! The level of detail in the log file output. Can be LOG_FILE_LOW_DETAIL, LOG_FILE_MIDDLE_DETAIL, LOG_FILE_HIGH_DETAIL, or LOG_FILE_ALL - int m_nLogFileDetail; - - //! The run-up equation used TODO 007 Finish surge and runup stuff - int m_nRunUpEquation; - - //! TODO 007 Used in WAVESETUP + SURGE + RUNUP Finish surge and runup stuff - int m_nLevel; - - //! The data type used by GDAL for integer operations, can be GDT_Byte, GDT_Int16, GDT_UInt16, GDT_Int32, or GDT_UInt32 - GDALDataType m_GDALWriteIntDataType; - - //! Thw data type used by GDAL for floating point operations, can be GDT_Byte, GDT_Int16, GDT_UInt16, GDT_Int32, GDT_UInt32, or GDT_Float32 - GDALDataType m_GDALWriteFloatDataType; - - //! The maximum integer value which GDAL can write, can be UINT8_MAX, INT16_MAX, UINT16_MAX, INT32_MAX, or UINT32_MAX, - long m_lGDALMaxCanWrite; - - //! The minimum integer value which GDAL can write, can be zero, INT16_MIN, INT32_MIN - long m_lGDALMinCanWrite; - - //! The number of the current iteration (time step) - unsigned long m_ulIter; - - //! The target number of iterations - unsigned long m_ulTotTimestep; - - //! A seed for each of the random number generators - unsigned long m_ulRandSeed[NUMBER_OF_RNGS]; - - //! The number of cells in the grid - unsigned long m_ulNumCells; - - //! The number of grid cells which are marked as sea, for this iteration - unsigned long m_ulThisIterNumSeaCells; - - //! The number of grid cells which are marked as coast, for this iteration - unsigned long m_ulThisIterNumCoastCells; - - //! The number of grid cells on which potential platform erosion occurs, for this iteration - unsigned long m_ulThisIterNumPotentialPlatformErosionCells; - - //! The number of grid cells on which actual platform erosion occurs, for this iteration - unsigned long m_ulThisIterNumActualPlatformErosionCells; - - //! The number of grid cells on which potential beach (unconsolidated sediment) erosion occurs, for this iteration - unsigned long m_ulThisIterNumPotentialBeachErosionCells; - - //! The number of grid cells on which actual beach (unconsolidated sediment) erosion occurs, for this iteration - unsigned long m_ulThisIterNumActualBeachErosionCells; - - //! The number of grid cells on which beach (unconsolidated sediment) deposition occurs, for this iteration - unsigned long m_ulThisIterNumBeachDepositionCells; - - //! The number of cells on which on-profile average potential shore platform erosion occurs - unsigned long m_ulTotPotentialPlatformErosionOnProfiles; - - //! The number of cells on which between-profile average potential shore platform erosion occurs - unsigned long m_ulTotPotentialPlatformErosionBetweenProfiles; - - //! The number of basement cells marked with as missing value - unsigned long m_ulMissingValueBasementCells; - - //! Used by CoastalME for unsigned long integer missing values - unsigned long m_ulMissingValue; - - //! Multiplier for duration units, to convert to hours - double m_dDurationUnitsMult; - - //! The north-west x coordinate, in the external coordinate reference system (CRS) - double m_dNorthWestXExtCRS; - - //! The north-west y coordinate, in the external coordinate reference system (CRS) - double m_dNorthWestYExtCRS; - - //! The south-east x coordinate, in the external coordinate reference system (CRS) - double m_dSouthEastXExtCRS; - - //! The south-east y coordinate, in the external coordinate reference system (CRS) - double m_dSouthEastYExtCRS; - - //! The area of the grid (in external CRS units) - double m_dExtCRSGridArea; - - //! Length of a cell side (in external CRS units) - double m_dCellSide; - - //! Area of a cell (in external CRS units) - double m_dCellArea; - - //! Length of a cell's diagonal (in external CRS units) - double m_dCellDiagonal; - - //! Inverse of m_dCellSide - double m_dInvCellSide; - - //! Inverse of m_dCellDiagonal - double m_dInvCellDiagonal; - - //! Duration of simulation, in hours - double m_dSimDuration; - - //! The length of an iteration (a timestep) in hours - double m_dTimeStep; - - //! Time simulated so far, in hours - double m_dSimElapsed; - - //! The time of the next save, in hours from the start of the simulation, if we are saving regularly - double m_dRegularSaveTime; - - //! The interval between regular saves, in hours - double m_dRegularSaveInterval; - - //! Save time, in hours from the start of the simukation, if we are not saving regularly - double m_dUSaveTime[SAVEMAX]; - - //! Last value returned by clock() - double m_dClkLast; - - //! Total elapsed CPU time - double m_dCPUClock; - - //! GDAL geotransformation info (see http://www.gdal.org/classGDALDataset.html) - double m_dGeoTransform[6]; - - //! Density of sea water in kg/m**3 - double m_dSeaWaterDensity; - - //! The start-of-simulation still water level (m) - double m_dInitialMeanSWL; - - //! The end-of-simulation still water (m), is same as m_dInitialMeanSWL unless SWL changes - double m_dFinalMeanSWL; - - //! If long-term SWL changes, the increment per timestep - double m_dDeltaSWLPerTimestep; - - //! The still water level for this timestep (this includes tidal changes and any long-term SWL change) - double m_dThisIterSWL; - - //! The mean still water level for this timestep (does not include tidal changes, but includes any long-term SWL change) - double m_dThisIterMeanSWL; - - //! If long-term SWL changes, the total change (m) so far since the start of simulation - double m_dAccumulatedSeaLevelChange; - - //! Minimum still water level (m) - double m_dMinSWLSoFar; - - //! Maximum still water level (m) - double m_dMaxSWLSoFar; - - //! TODO 007 Finish surge and runup stuff - double m_dThisIterDiffTotWaterLevel; - - //! TODO 007 Finish surge and runup stuff - double m_dThisIterDiffWaveSetupWaterLevel; - - //! TODO 007 Finish surge and runup stuff - double m_dThisIterDiffWaveSetupSurgeWaterLevel; - - //! TODO 007 Finish surge and runup stuff - double m_dThisIterDiffWaveSetupSurgeRunupWaterLevel; - - //! The height of breaking waves (m) - double m_dBreakingWaveHeight; - - //! Deep water wave speed (m/s) - double m_dC_0; - - //! Deep water wave length (m) - double m_dL_0; - - //! Start depth for wave calculations - double m_dWaveDepthRatioForWaveCalcs; - - //! Breaking wave height-to-depth ratio - double m_dBreakingWaveHeightDepthRatio; - - //! Deep water wave height (m) for all sea cells - double m_dAllCellsDeepWaterWaveHeight; - - //! Deep water wave angle for all sea cells - double m_dAllCellsDeepWaterWaveAngle; - - //! Deep water wave period for all sea cells - double m_dAllCellsDeepWaterWavePeriod; - - //! Maximum deep water wave height - double m_dMaxUserInputWaveHeight; - - //! Used to constrain depth of closure - double m_dMaxUserInputWavePeriod; - - //! Coast platform resistance to erosion R, see Walkden & Hall, 2011 - double m_dR; - - //! The D50 for fine sediment - double m_dD50Fine; - - //! The D50 for sand sediment - double m_dD50Sand; - - //! The D50 for coarse sediment - double m_dD50Coarse; - - //! The density of unconsolidated beach sediment (kg/m**3) - double m_dBeachSedimentDensity; - - //! The porosity of unconsolidated beach sediment (0-1) - double m_dBeachSedimentPorosity; - - //! The relative erodibility (0-1) of fine unconsolidated beach sediment - double m_dFineErodibility; - - //! The relative erodibility (0-1) of sand unconsolidated beach sediment - double m_dSandErodibility; - - //! The relative erodibility (0-1) of coarse unconsolidated beach sediment - double m_dCoarseErodibility; - - //! Relative erodibility of fine unconsolidated beach sediment, normalized - double m_dFineErodibilityNormalized; - - //! Relative erodibility of sand unconsolidated beach sediment, normalized - double m_dSandErodibilityNormalized; - - //! Relative erodibility of coarse unconsolidated beach sediment, normalized - double m_dCoarseErodibilityNormalized; - - //! Transport parameter KLS in the CERC equation - double m_dKLS; - - //! Transport parameter for the Kamphuis equation - double m_dKamphuis; - - //! Gravitational acceleration (m**2/sec) - double m_dG; - - //! For beach erosion/deposition, conversion from immersed weight to bulk volumetric (sand and voids) transport rate (Leo Van Rijn) TODO 007 Need date of reference - double m_dInmersedToBulkVolumetric; - - //! Depth of closure (in m) TODO 007 can be calculated using Hallermeier, R.J. (1978) or Birkemeier (1985) TODO 045 This needs to be a user decision - double m_dDepthOfClosure; - - //! Average spacing of the coastline-normal profiles, in m - double m_dCoastNormalSpacing; - - //! Average spacing of the coastline-normal profiles on interventions, in m - double m_dCoastNormalInterventionSpacing; - - //! Length of the coastline-normal profiles, in m - double m_dCoastNormalLength; - - //! Approximate minimum spacing (m) between wave transects (real and synthetic) for wave interpolation densification - double m_dSyntheticTransectSpacing; - - //! Total sea depth (m) for this iteration - double m_dThisIterTotSeaDepth; - - //! Total potential platform erosion (all size classes of consolidated sediment) for this iteration (depth in m) - double m_dThisIterPotentialPlatformErosion; - - //! Total actual platform erosion (fine consolidated sediment) for this iteration (depth in m) - double m_dThisIterActualPlatformErosionFineCons; - - //! Total actual platform erosion (sand consolidated sediment) for this iteration (depth in m) - double m_dThisIterActualPlatformErosionSandCons; - - //! Total actual platform erosion (coarse consolidated sediment) for this iteration (depth in m) - double m_dThisIterActualPlatformErosionCoarseCons; - - //! Total potential beach erosion (all size classes of unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterPotentialBeachErosion; - - //! Total actual beach erosion (fine unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterBeachErosionFine; - - //! Total actual beach erosion (sand unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterBeachErosionSand; - - //! Total actual beach erosion (coarse unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterBeachErosionCoarse; - - //! Total beach deposition (sand unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterBeachDepositionSand; - - //! Total beach deposition (coarse unconsolidated sediment) for this iteration (depth in m) - double m_dThisIterBeachDepositionCoarse; - - //! Total fine unconsolidated sediment in suspension for this iteration (depth in m) - double m_dThisIterFineSedimentToSuspension; - - //! Total unconsolidated sediment from beach erosion (all size classes) lost from the grid this iteration (depth in m) - double m_dThisIterPotentialSedLostBeachErosion; - - //! Total fine unconsolidated sediment lost from the grid this iteration (depth in m) - double m_dThisIterLeftGridUnconsFine; - - //! Total sand unconsolidated sediment lost from the grid this iteration (depth in m) - double m_dThisIterLeftGridUnconsSand; - - //! Total coarse unconsolidated sediment lost from the grid this iteration (depth in m) - double m_dThisIterLeftGridUnconsCoarse; - - //! Total fine sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) - double m_dThisIterCliffCollapseFineErodedDuringDeposition; - - //! Total sand sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) - double m_dThisIterCliffCollapseSandErodedDuringDeposition; - - //! Total coarse sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) - double m_dThisIterCliffCollapseCoarseErodedDuringDeposition; - - //! Error term: if we are unable to deposit enough unconslidated sand on polygon(s), this is held over to be deposited the next iteration - double m_dDepositionSandDiff; - - //! Error term: if we are unable to deposit enough unconslidated coarse on polygon(s), this is held over to be deposited the next iteration - double m_dDepositionCoarseDiff; - - //! Maximum value of deoth over DB, is used in erosion potential look-up function - double m_dDepthOverDBMax; - - //! Total potential platform erosion on profiles - double m_dTotPotentialPlatformErosionOnProfiles; - - //! Total potential platform erosion between profiles - double m_dTotPotentialPlatformErosionBetweenProfiles; - - //! Maximum slope on coastline-normal profiles - double m_dProfileMaxSlope; - - //! Maximum elevation of beach above SWL (m) - double m_dMaxBeachElevAboveSWL; - - //! Resistance of cliff to notch erosion - double m_dCliffErosionResistance; - - //! Notch overhang (i.e. length of horizontal incision at the apex elevation) to initiate collapse (m) - double m_dNotchIncisionAtCollapse; - - //! Elevation (m) of the apex of any cliff notches created during this iteration - double m_dThisIterNewNotchApexElev; - - //! Distance of notch base below SWL (m) - double m_dNotchApexAboveMHW; - - //! Scale parameter A for cliff deposition (m^(1/3)), may be zero for auto-calculation - double m_dCliffDepositionA; - - //! Planview width of cliff collapse talus (m) - double m_dCliffDepositionPlanviewWidth; - - //! Planview length of cliff deposition talus (m) - double m_dCliffTalusMinDepositionLength; - - //! Minimum height of the landward end of cliff collapse talus, as a fraction of cliff elevation - double m_dMinCliffTalusHeightFrac; - - //! This-iteration total of fine unconsolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionFineUncons; - - //! This-iteration total of sand unconsolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionSandUncons; - - //! This-iteration total of coarse unconsolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionCoarseUncons; - - //! This-iteration total of fine consolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionFineCons; - - //! This-iteration total of sand consolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionSandCons; - - //! This-iteration total of coarse consolidated sediment produced by cliff collapse (m^3) - double m_dThisIterCliffCollapseErosionCoarseCons; - - //! This-iteration total of sand unconsolidated sediment deposited due to cliff collapse (m^3) - double m_dThisIterUnconsSandCliffDeposition; - - //! This-iteration total of coarse unconsolidated sediment deposited due to cliff collapse (m^3) - double m_dThisIterUnconsCoarseCliffDeposition; - - //! Random factor for spacing of along-coast normals - double m_dCoastNormalRandSpacingFactor; - - //! Berm height i.e. height above SWL of start of depositional Dean profile - double m_dDeanProfileStartAboveSWL; - - //! The value for floating-point missing values, as read from GIS input files - double m_dGISMissingValue; - - //! Used by CoastalME for floating-point missing values - double m_dMissingValue; - - //! Number of hours after which deep water wave data wraps - double m_dWaveDataWrapHours; - - //! This-iteration highest elevation of DEM - double m_dThisIterTopElevMax; - - //! This-iteration lowest elevation of DEM - double m_dThisIterTopElevMin; - - //! Depth (m) of fine unconsolidated sediment added, at this iteration - double m_dThisiterUnconsFineInput; - - //! Depth (m) of sand unconsolidated sediment added, at this iteration - double m_dThisiterUnconsSandInput; - - //! Depth (m) of coarse unconsolidated sediment added, at this iteration - double m_dThisiterUnconsCoarseInput; - - //! Depth (m) of fine suspended sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterSuspFineAllCells; - - //! Depth (m) of fine suspended sediment at the start of the simulation (only cells in polygons) - double m_dStartIterSuspFineInPolygons; - - //! Depth (m) of fine unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterUnconsFineAllCells; - - //! Depth (m) of sand unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterUnconsSandAllCells; - - //! Depth (m) of coarse unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterUnconsCoarseAllCells; - - //! Depth (m) of fine consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterConsFineAllCells; - - //! Depth (m) of sand consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterConsSandAllCells; - - //! Depth (m) of coarse consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) - double m_dStartIterConsCoarseAllCells; - - //! Total fine unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalFineUnconsInPolygons; - - //! Total sand unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalSandUnconsInPolygons; - - //! Total coarse unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalCoarseUnconsInPolygons; - - //! Total fine consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalFineConsInPolygons; - - //! Total sand consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalSandConsInPolygons; - - //! Total coarse consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) - double m_dTotalCoarseConsInPolygons; - - //! Depth of unconsolidated sand sediment that could not be deposited during the last iteration, carried forward to this iteration - double m_dUnconsSandNotDepositedLastIter; - - //! Depth of unconsolidated coarse sediment that could not be deposited during the last iteration, carried forward to this iteration - double m_dUnconsCoarseNotDepositedLastIter; - - //! This iteration's Mean High Water (MHW) elevation, calculated using a moving time window. If we have no tide data, this is set to mean SWL - double m_dThisIterMHWElev; - - //! Slope limit for cliff toe detection - double m_dSlopeThresholdForCliffToe; - - // These grand totals are all long doubles. The aim is to minimize rounding errors when many very small numbers are added to a single much larger number, see e.g. http://www.ddj.com/cpp/184403224 - //! All-simulation total of potential platform erosion (m), all size classes - long double m_ldGTotPotentialPlatformErosion; - - //! All-simulation total of fine sediment actual platform erosion (m) - long double m_ldGTotFineActualPlatformErosion; - - //! All-simulation total of sand sediment actual platform erosion (m) - long double m_ldGTotSandActualPlatformErosion; - - //! All-simulation total of coarse sediment actual platform erosion (m) - long double m_ldGTotCoarseActualPlatformErosion; - - //! All-simulation total of potential sediment lost via beach (unconsolidated) sediment movement (m), all size classes - long double m_ldGTotPotentialSedLostBeachErosion; - - //! All-simulation total of fine sediment lost via beach (unconsolidated) sediment movement (m) - long double m_ldGTotActualFineLostBeachErosion; - - //! All-simulation total of sand sediment lost via beach (unconsolidated) sediment movement (m) - long double m_ldGTotActualSandLostBeachErosion; - - //! All-simulation total of coarse sediment lost via beach (unconsolidated) sediment movement (m) - long double m_ldGTotActualCoarseLostBeachErosion; - - //! All-simulation total of sand sediment lost via cliff collapse (m) - long double m_ldGTotSandSedLostCliffCollapse; - - //! All-simulation total of coarse sediment lost via cliff collapse (m) - long double m_ldGTotCoarseSedLostCliffCollapse; - - //! All-simulation total of fine sediment from cliff collapse (m) - long double m_ldGTotCliffCollapseFine; - - //! All-simulation total of sand sediment from cliff collapse (m) - long double m_ldGTotCliffCollapseSand; - - //! All-simulation total of coarse sediment from cliff collapse (m) - long double m_ldGTotCliffCollapseCoarse; - - //! All-simulation total of fine sediment moved to suspension, due to cliff collapse (m) - long double m_ldGTotCliffTalusFineToSuspension; - - //! All-simulation total of sand sediment deposited as talus following cliff collapse (m) - long double m_ldGTotCliffTalusSandDeposition; - - //! All-simulation total of coarse sediment deposited as talus following cliff collapse (m) - long double m_ldGTotCliffTalusCoarseDeposition; - - //! All-simulation total of fine sediment eroded during talus deposition following cliff collapse (m) - long double m_ldGTotCliffCollapseFineErodedDuringDeposition; - - //! All-simulation total of sand sediment eroded during talus deposition following cliff collapse (m) - long double m_ldGTotCliffCollapseSandErodedDuringDeposition; - - //! All-simulation total of coarse sediment eroded during talus deposition following cliff collapse (m) - long double m_ldGTotCliffCollapseCoarseErodedDuringDeposition; - - //! All-simulation total of potential beach erosion (m), all size classes - long double m_ldGTotPotentialBeachErosion; - - //! All-simulation total of fine sediment eroded during beach (unconsolidated sediment) movement (m) - long double m_ldGTotActualFineBeachErosion; - - //! All-simulation total of sand sediment eroded during beach (unconsolidated sediment) movement (m) - long double m_ldGTotActualSandBeachErosion; - - //! All-simulation total of coarse sediment eroded during beach (unconsolidated sediment) movement (m) - long double m_ldGTotActualCoarseBeachErosion; - - //! All-simulation total of sand sediment deposited during beach (unconsolidated sediment) movement (m) - long double m_ldGTotSandBeachDeposition; - - //! All-simulation total of coarse sediment deposited during beach (unconsolidated sediment) movement (m) - long double m_ldGTotCoarseBeachDeposition; - - //! All-simulation total of suspended sediment (m) - long double m_ldGTotSuspendedSediment; - - //! All-simulation total of shortfall in unconsolidated sand sediment deposition (m, not currently used) - long double m_ldGTotSandDepositionDiff; - - //! All-simulation total of shortfall in unconsolidated coarse sediment deposition (m, not currently used) - long double m_ldGTotCoarseDepositionDiff; - - //! All-simulation total of fine sediment input (m) - long double m_ldGTotFineSedimentInput; - - //! All-simulation total of sand sediment input (m) - long double m_ldGTotSandSedimentInput; - - //! All-simulation total of coarse sediment input (m) - long double m_ldGTotCoarseSedimentInput; - - //! The CME folder - string m_strCMEDir; - - //! Folder for the CME .ini file - string m_strCMEIni; - - //! An email addresx to which to send end-of-simulation messages - string m_strMailAddress; - - //! Folder in which the CME data file is found - string m_strDataPathName; - - //! Raster GIS output format - string m_strRasterGISOutFormat; - - //! Vector GIS output format - string m_strVectorGISOutFormat; - - //! Name of initial basement DEM file - string m_strInitialBasementDEMFile; - - //! Name of initial landform file - string m_strInitialLandformFile; - - //! Name of intervention class file - string m_strInterventionClassFile; - - //! Name of intervention height file - string m_strInterventionHeightFile; - - //! Name of initial suspended sediment file - string m_strInitialSuspSedimentFile; - - //! Name of SCAPE shape function file - string m_strSCAPEShapeFunctionFile; - - //! Name of tide data file - string m_strTideDataFile; - - //! Name of output log file - string m_strLogFile; - - //! Path for all output files - string m_strOutPath; - - //! Name of main output file - string m_strOutFile; - - //! GDAL code for the basement DEM raster file type - string m_strGDALBasementDEMDriverCode; - - //! GDAL description of the basement DEM raster file type - string m_strGDALBasementDEMDriverDesc; - - //! GDAL projection string for the basement DEM raster file - string m_strGDALBasementDEMProjection; - - //! GDAL data type for the basement DEM raster file - string m_strGDALBasementDEMDataType; - - //! GDAL code for the for the initial landform class raster file - string m_strGDALLDriverCode; - - //! GDAL description of the initial landform class raster file - string m_strGDALLDriverDesc; - - //! GDAL projection string for the initial landform class raster file - string m_strGDALLProjection; - - //! GDAL data type for the initial landform class raster file - string m_strGDALLDataType; - - //! GDAL code for the initial intervention class raster file - string m_strGDALICDriverCode; - - //! GDAL description of the initial intervention class raster file - string m_strGDALICDriverDesc; - - //! GDAL projection string for the initial intervention class raster file - string m_strGDALICProjection; - - //! GDAL data type of the initial intervention class raster file - string m_strGDALICDataType; - - //! GDAL code for the initial intervention height raster file - string m_strGDALIHDriverCode; - - //! GDAL description for the initial intervention height raster file - string m_strGDALIHDriverDesc; - - //! GDAL projection string for the initial intervention height raster file - string m_strGDALIHProjection; - - //! GDAL data type for the initial intervention height raster file - string m_strGDALIHDataType; - - //! GDAL code for the initial water depth raster file - string m_strGDALIWDriverCode; - - //! GDAL description for the initial water depth raster file - string m_strGDALIWDriverDesc; - - //! GDAL projection string for the initial water depth raster file - string m_strGDALIWProjection; - - //! GDAL data type for the initial water depth raster file - string m_strGDALIWDataType; - - //! GDAL code for the initial suspended sediment raster file - string m_strGDALISSDriverCode; - - //! GDAL description for the initial suspended sediment raster file - string m_strGDALISSDriverDesc; - - //! GDAL projection string for the initial suspended sediment raster file - string m_strGDALISSProjection; - - //! GDAL data type for the initial suspended sediment raster file - string m_strGDALISSDataType; - - //! GDAL code for the deep water wave stations vector file - string m_strOGRDWWVDriverCode; - - // GDAL description for the deep water wave stations vector file - string m_strOGRDWWVDriverDesc; - - //! GDAL geometry for the deep water wave stations vector file - string m_strOGRDWWVGeometry; - - //! GDAL data type for the deep water wave stations vector file - string m_strOGRDWWVDataType; - - //! GDAL code for the sediment input event locations vector file - string m_strOGRSedInputDriverCode; - - // GDAL description for the sediment input event locations vector file - string m_strOGRSedInputDriverDesc; - - //! GDAL geometry for the sediment input event locations vector file - string m_strOGRSedInputGeometry; - - //! GDAL data type for the sediment input event locations vector file - string m_strOGRSedInputDataType; - - //! GDAL code for the flood input locations point or vector file - string m_strOGRFloodDriverCode; - - // GDAL description for the flood input locations point or vector file - string m_strOGRFloodDriverDesc; - - //! GDAL geometry for the flood input locations point or vector file - string m_strOGRFloodGeometry; - - //! GDAL data type for the flood input locations point or vector file - string m_strOGRFloodDataType; - - //! GDAL raster output driver long name - string m_strGDALRasterOutputDriverLongname; - - //! GDAL raster output driver file extension - string m_strGDALRasterOutputDriverExtension; - - //! GDAL-OGR vector output drive file extension - string m_strOGRVectorOutputExtension; - - //! The name of this simulation - string m_strRunName; - - //! The duration units for this simulation - string m_strDurationUnits; - - //! The name of the deep water wave stations shape file - string m_strDeepWaterWaveStationsShapefile; - - //! The name of the deep water wave stations time series file - string m_strDeepWaterWavesInputFile; - - //! The name of the sediment input events shape file - string m_strSedimentInputEventShapefile; - - //! The name of the sediment input events time series file - string m_strSedimentInputEventFile; - - //! The name of the flood loction events shape file - string m_strFloodLocationShapefile; - - //! System start-simulation time - time_t m_tSysStartTime; - - //! System finish-simulation time - time_t m_tSysEndTime; - - //! The main output file stream - ofstream OutStream; - - //! Sea area time series file output stream - ofstream SeaAreaTSStream; - - //! SWL time series file output stream - ofstream SWLTSStream; - - //! Shore platform erosion time series file output stream - ofstream PlatformErosionTSStream; - - //! Cliff collapse erosion time series file output stream - ofstream CliffCollapseErosionTSStream; - - //! Cliff collapse deposition time series file output stream - ofstream CliffCollapseDepositionTSStream; - - //! Cliff collapse net change (erosion - deposition) time series file output stream - ofstream CliffCollapseNetChangeTSStream; - - //! Beach sediment erosion time series file output stream - ofstream BeachErosionTSStream; - - //! Beach sediment deposition time series file output stream - ofstream BeachDepositionTSStream; - - //! Beach sediment net change (erosion - deposition) time series file output stream - ofstream BeachSedimentNetChangeTSStream; - - //! Fine sediment in suspension time series file output stream - ofstream FineSedSuspensionTSStream; - - //! Flood setup surge time series file output stream - ofstream FloodSetupSurgeTSStream; - - //! Flood setup surge runup time series file output stream - ofstream FloodSetupSurgeRunupTSStream; - - //! Cliff notch elevation time series file output stream - ofstream CliffNotchElevTSStream; - - //! One element per layer: has the consolidated sediment of this layer been changed during this iteration? - vector m_bConsChangedThisIter; - - //! One element per layer: has the consolidated sediment of this layer been changed during this iteration? - vector m_bUnconsChangedThisIter; - - //! The numbers of the profiles which are to be saved - vector m_VnProfileToSave; - - //! ID for deep water wave station, this corresponds with the ID in the wave time series file - vector m_VnDeepWaterWaveStationID; - - //! ID for sediment input location, this corresponds with the ID in the sediment input time series file - vector m_VnSedimentInputLocationID; - - //! ID for flood location - vector m_VnFloodLocationID; - - //! Savitzky-Golay shift index for the coastline vector(s) - vector m_VnSavGolIndexCoast; - - //! Timesteps at which to save profiles - vector m_VulProfileTimestep; - - //! Calculate deep water wave values at these timesteps - vector m_VlDeepWaterWaveValuesAtTimestep; - - //! Elevations for raster slice output - vector m_VdSliceElev; - - //! For erosion potential lookup - vector m_VdErosionPotential; - - //! For erosion potential lookup - vector m_VdDepthOverDB; - - //! Savitzky-Golay filter coefficients for the coastline vector(s) - vector m_VdSavGolFCRWCoast; - - //! Savitzky-Golay filter coefficients for the profile vectors - vector m_VdSavGolFCGeomProfile; - - //! Tide data: one record per timestep, is the change (m) from still water level for that timestep - vector m_VdTideData; - - //! X coordinate (grid CRS) for deep water wave station - vector m_VdDeepWaterWaveStationX; - - //! Y coordinate (grid CRS) for deep water wave station - vector m_VdDeepWaterWaveStationY; - - //! This-iteration wave height at deep water wave station - vector m_VdThisIterDeepWaterWaveStationHeight; - - //! This-iteration wave orientation at deep water wave station - vector m_VdThisIterDeepWaterWaveStationAngle; - - //! This-iteration wave period at deep water wave station - vector m_VdThisIterDeepWaterWaveStationPeriod; - - //! Time series of wave heights at deep water wave station - vector m_VdTSDeepWaterWaveStationHeight; - - //! Time series of wave orientation at deep water wave station - vector m_VdTSDeepWaterWaveStationAngle; - - //! Time series of wave period at deep water wave station - vector m_VdTSDeepWaterWaveStationPeriod; - - //! Storage for wave transect points (real and synthetic) for debug output - vector m_VAllTransectsWithSynthetic; - - //! X coordinate (grid CRS) for sediment input event - vector m_VdSedimentInputLocationX; - - //! X coordinate (grid CRS) for sediment input event - vector m_VdSedimentInputLocationY; - - //! X coordinate (grid CRS) for total water level flooding - vector m_VdFloodLocationX; - - //! X coordinate (grid CRS) for total water level flooding - vector m_VdFloodLocationY; - - //! The name of the initial fine-sized unconsolidated sediment GIS file - vector m_VstrInitialFineUnconsSedimentFile; - - //! The name of the initial sand-sized unconsolidated sediment GIS file - vector m_VstrInitialSandUnconsSedimentFile; - - //! The name of the initial coarse-sized unconsolidated sediment GIS file - vector m_VstrInitialCoarseUnconsSedimentFile; - - //! The name of the initial fine-sized consolidated sediment GIS file - vector m_VstrInitialFineConsSedimentFile; - - //! The name of the initial sand-sized consolidated sediment GIS file - vector m_VstrInitialSandConsSedimentFile; - - //! The name of the initial coarse-sized consolidated sediment GIS file - vector m_VstrInitialCoarseConsSedimentFile; - - //! GDAL driver code for the initial unconsolidated fine sediment GIS data - vector m_VstrGDALIUFDriverCode; - - //! GDAL driver description for the initial unconsolidated fine sediment GIS data - vector m_VstrGDALIUFDriverDesc; - - //! GDAL projection for the initial unconsolidated fine sediment GIS data - vector m_VstrGDALIUFProjection; - - //! GDAL data type for the initial unconsolidated fine sediment GIS data - vector m_VstrGDALIUFDataType; - - //! GDAL driver code for the initial unconsolidated sand sediment GIS data - vector m_VstrGDALIUSDriverCode; - - //! GDAL driver description for the initial unconsolidated sand sediment GIS data - vector m_VstrGDALIUSDriverDesc; - - //! GDAL projection for the initial unconsolidated sand sediment GIS data - vector m_VstrGDALIUSProjection; - - //! GDAL data type for the initial unconsolidated sand sediment GIS data - vector m_VstrGDALIUSDataType; - - //! GDAL driver code for the initial unconsolidated coarse sediment GIS data - vector m_VstrGDALIUCDriverCode; - - //! GDAL driver description for the initial unconsolidated coarse sediment GIS data - vector m_VstrGDALIUCDriverDesc; - - //! GDAL projection for the initial unconsolidated coarse sediment GIS data - vector m_VstrGDALIUCProjection; - - //! GDAL data type for the initial unconsolidated coarse sediment GIS data - vector m_VstrGDALIUCDataType; - - //! GDAL driver code for the initial consolidated fine sediment GIS data - vector m_VstrGDALICFDriverCode; - - //! GDAL driver description for the initial consolidated fine sediment GIS data - vector m_VstrGDALICFDriverDesc; - - //! GDAL projection for the initial consolidated fine sediment GIS data - vector m_VstrGDALICFProjection; - - //! GDAL data type for the initial consolidated fine sediment GIS data - vector m_VstrGDALICFDataType; - - //! GDAL driver code for the initial consolidated sand sediment GIS data - vector m_VstrGDALICSDriverCode; - - //! GDAL driver description for the initial consolidated sand sediment GIS data - vector m_VstrGDALICSDriverDesc; - - //! GDAL dprojection for the initial consolidated sand sediment GIS data - vector m_VstrGDALICSProjection; - - //! GDAL data type for the initial consolidated sand sediment GIS data - vector m_VstrGDALICSDataType; - - //! GDAL driver code for the initial consolidated coarse sediment GIS data - vector m_VstrGDALICCDriverCode; - - //! GDAL driver decription for the initial consolidated coarse sediment GIS data - vector m_VstrGDALICCDriverDesc; - - //! GDAL projection for the initial consolidated coarse sediment GIS data - vector m_VstrGDALICCProjection; - - //! GDAL data type for the initial consolidated coarse sediment GIS data - vector m_VstrGDALICCDataType; - - //! Pointer to the raster grid object - CGeomRasterGrid* m_pRasterGrid; - - //! The coastline objects - vector m_VCoast; - - //! Coastline (external CRS) at the highest SWL so far during this simulation - vector m_VHighestSWLCoastLine; - - //! Coastline (external CRS) at the lowest SWL so far during this simulation - vector m_VLowestSWLCoastLine; - - //! The traced cliff toe lines (in external CRS) - vector m_VCliffToe; - - //! TODO 007 Finish surge and runup stuff - vector m_VFloodWaveSetupSurge; - - //! TODO 007 Finish surge and runup stuff - vector m_VFloodWaveSetupSurgeRunup; - - //! Edge cells - vector m_VEdgeCell; - - //! The grid edge that each edge cell belongs to - vector m_VEdgeCellEdge; - - //! The location to compute the total water level for flooding - vector m_VCellFloodLocation; - - //! Sediment input events - vector m_pVSedInputEvent; - - //! The c++11 random number generators - default_random_engine m_Rand[NUMBER_OF_RNGS]; - - //! c++11 unit normal distribution (mean = 0, stdev = 1) - normal_distribution m_dGetFromUnitNormalDist{0.0, 1.0}; - - private: - // Input and output routines - int nHandleCommandLineParams(int, char const*[]); - bool bReadIniFile(void); - bool bReadRunDataFile(void); - bool bReadYamlFile(void); - bool bDetectFileFormat(string const& strFileName, bool& bIsYaml); - bool bConfigureFromDatFile(CConfiguration& config); - bool bConfigureFromYamlFile(CConfiguration& config); - bool bApplyConfiguration(CConfiguration const& config); - bool bOpenLogFile(void); - bool bSetUpTSFiles(void); - void WriteStartRunDetails(void); - bool bWritePerTimestepResults(void); - bool bWritePerTimestepResultsFixedWidth(void); - bool bWritePerTimestepResultsCSV(void); - bool bWriteTSFiles(void); - int nWriteEndRunDetails(void); - int nReadShapeFunctionFile(void); - int nReadWaveStationInputFile(int const); - int nReadSedimentInputEventFile(void); - int nReadTideDataFile(void); - int nSaveProfile(int const, CGeomProfile const*, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; - bool bWriteProfileData(int const, CGeomProfile const*, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; - int nSaveParProfile(int const, CGeomProfile const*, int const, int const, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; - bool bWriteParProfileData(int const, int const, int const, int const, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; - void WriteLookUpData(void); - - // GIS input and output stuff - void InitializeGDALPerformance(void); - int nReadRasterBasementDEM(void); - int nReadRasterGISFile(int const, int const); - int nReadVectorGISFile(int const); - bool bWriteRasterGISFile(int const, string const*, int const = 0, double const = 0); - bool bWriteVectorGISFile(int const, string const*); - void GetRasterOutputMinMax(int const, double&, double&, int const, double const); - void SetRasterFileCreationDefaults(void); - int nInterpolateWavesToPolygonCells(vector const*, vector const*, vector const*, vector const*, vector const*); - - // Initialization - bool bCreateErosionPotentialLookUp(vector*, vector*, vector*); - void CalcMHWElevation(int const); - - // Top-level simulation routines - static int nUpdateIntervention(void); - int nCheckForSedimentInputEvent(void); - int nCalcExternalForcing(void); - int nInitGridAndCalcStillWaterLevel(void); - int nLocateSeaAndCoasts(void); - int nLocateFloodAndCoasts(void); - int nAssignLandformsForAllCoasts(void); - int nAssignLandformsForAllCells(void); - int nDoAllPropagateWaves(void); - void GenerateSyntheticTransects(vector const*, vector*); - int nDoAllShorePlatFormErosion(void); - int nDoAllWaveEnergyToCoastLandforms(void); - int nDoCliffCollapse(int const, CRWCliff *, double&, double&, double&, int&, double&, double&); - void DoCliffCollapseTalusDeposition(int const, CRWCliff const*, double const, double const, int const); - int nMoveCliffTalusToUnconsolidated(void); - int nUpdateGrid(void); - - // For cliff toe location - int nLocateCliffToe(void); - void nCalcSlopeAtAllCells(void); - void nLocateCliffCell(void); - void nTraceSeawardCliffEdge(void); - void nValidateCliffToeEdges(void); - CGeomLine nValidateCliffToeDirection(CGeomLine& CliffEdge, bool bReverse); - void nRemoveSmallCliffIslands(int const); - - // Lower-level simulation routines - void FindAllSeaCells(void); - int FindAllInundatedCells(void); - void CellByCellFillSea(int const, int const); - void FloodFillLand(int const, int const); - int nTraceCoastLine(unsigned int const, int const, int const, vector*, vector const*); - int nTraceAllCoasts(void); - int nTraceFloodCoastLine(unsigned int const, int const, int const, vector*, vector const*); - int nTraceAllFloodCoasts(void); - void DoCoastCurvature(int const, int const); - int nCheckAndMarkAllProfiles(void); - int nCreateAllProfiles(void); - void LocateAndCreateProfiles(int const, int&, vector*, vector> const*); - int nCreateProfile(int const, int const, int const, int const, bool const, CGeom2DIPoint const*); - int nLocateAndCreateGridEdgeProfile(bool const, int const, int&); - void MarkProfilesOnGrid(int const, int&); - void CheckForIntersectingProfiles(void); - static bool bCheckForIntersection(CGeomProfile* const, CGeomProfile* const, int&, int&, double&, double&, double&, double&); - void MergeProfilesAtFinalLineSegments(int const, CGeomProfile*, CGeomProfile*, int const, int const, double const, double const, double const, double const); - void TruncateOneProfileRetainOtherProfile(int const, CGeomProfile*, CGeomProfile*, double, double, int, int, bool const); - int nInsertPointIntoProfilesIfNeededThenUpdate(int const, CGeomProfile*, double const, double const, int const, CGeomProfile*, int const, bool const); - void TruncateProfileAndAppendNew(int const, CGeomProfile*, int const, vector const*, vector>> const*); - void CreateRasterizedProfile(int const, CGeomProfile*, vector*, vector*, bool&, bool&, bool&, bool&, bool&, bool&); - static void CalcDeanProfile(vector*, double const, double const, double const, bool const, int const, double const); - static double dSubtractProfiles(vector const*, vector const*, vector const*); - int nCalcPotentialPlatformErosionOnProfile(int const, CGeomProfile*); - int nCalcPotentialPlatformErosionBetweenProfiles(int const, CGeomProfile*, int const); - void ConstructParallelProfile(int const, int const, int const, int const, int const, vector* const, vector*, vector *); - double dCalcBeachProtectionFactor(int const, int const, double const); - void FillInBeachProtectionHolesAndRemoveLegacyCliffs(void); - void FillPotentialPlatformErosionHoles(void); - void DoActualPlatformErosionOnCell(int const, int const); - double dLookUpErosionPotential(double const); - static CGeom2DPoint PtChooseEndPoint(int const, CGeom2DPoint const*, CGeom2DPoint const*, double const, double const, double const, double const); - int nGetCoastNormalEndPoint(int const, int const, int const, CGeom2DPoint const*, double const, CGeom2DPoint *, CGeom2DIPoint *, bool const); - int nLandformToGrid(int const, int const); - int nCalcWavePropertiesOnProfile(int const, int const, CGeomProfile*, vector*, vector*, vector*, vector*, vector*); - int nGetThisProfileElevationsForCShore(int const, CGeomProfile*, int const, vector*, vector*, vector*); - int nCreateCShoreInfile(int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, double const, double const, double const, double const, double const, double const, double const, double const, vector const*, vector const*, vector const*); - int nReadCShoreOutput(int const, string const*, int const, int const, vector const*, vector*); - /* static */ void InterpolateCShoreOutput(vector const*, int const, int const, vector*, vector*, vector*, vector*, vector*, vector*, vector*, vector*, vector*); - static double dCalcWaveAngleToCoastNormal(double const, double const, int const); - void CalcCoastTangents(int const); - void InterpolateWavePropertiesBetweenProfiles(int const, int const); - void InterpolateWaveHeightToCoastPoints(int const); - // void InterpolateWavePropertiesToCells(int const, int const, int const); - void ModifyBreakingWavePropertiesWithinShadowZoneToCoastline(int const, int const); - static double dCalcCurvature(int const, CGeom2DPoint const*, CGeom2DPoint const*, CGeom2DPoint const*); - void CalcD50AndFillWaveCalcHoles(void); - int nDoAllShadowZones(void); - static bool bOnOrOffShoreAndUpOrDownCoast(double const, double const, int const, bool&); - static CGeom2DIPoint PtiFollowWaveAngle(CGeom2DIPoint const*, double const, double&); - // int nFindAllShadowZones(void); - int nCellByCellFillShadowZone(int const, int const, CGeom2DIPoint const*, CGeom2DIPoint const*, CGeom2DIPoint const*); - void DoShadowZoneAndDownDriftZone(int const, int const, int const, int const); - void ProcessDownDriftCell(int const, int const, int const, double const, int const); - void ProcessShadowZoneCell(int const, int const, int const, CGeom2DIPoint const*, int const, int const, int const); - int nCreateAllPolygons(void); - void RasterizePolygonJoiningLine(int const, CGeom2DIPoint const*, CGeom2DIPoint const*, int const); - // static bool bIsWithinPolygon(CGeom2DPoint const*, vector const*); - // static CGeom2DPoint PtFindPointInPolygon(vector const*, int const); - void MarkPolygonCells(void); - int nDoPolygonSharedBoundaries(void); - void DoAllPotentialBeachErosion(void); - int nDoAllActualBeachErosionAndDeposition(void); - int nDoParallelProfileUnconsErosion(CGeomCoastPolygon *, int const, int const, int const, int const, int const, int const, vector const*, vector const*, double&, double&, double&); - void ErodeCellBeachSedimentSupplyLimited(int const, int const, int const, int const, double const, double&); - int nDoUnconsErosionOnPolygon(int const, CGeomCoastPolygon *, int const, double const, double&); - int nDoUnconsDepositionOnPolygon(int const, CGeomCoastPolygon *, int const, double, double&); - void CalcDepthOfClosure(void); - int nInterpolateAllDeepWaterWaveValues(void); - int nSetAllCoastpointDeepWaterWaveValues(void); - int nDoSedimentInputEvent(int const); - void AllPolygonsUpdateStoredUncons(int const); - bool bIsInterventionCell(int const, int const) const; - bool bSurroundedByDriftCells(int const, int const); - bool bElevAboveDeanElev(int const, int const, double const, CRWCellLandform const*); - // void CreatePolygonIndexIDSeq(int const); - int nDoMultipleCoastlines(void); - int nTruncateProfilesDifferentCoasts(int const, int const, int const, int const, int const, int const); - int nTruncateProfileHitDifferentCoast(int const, int const, int const, int const); - int nTruncateProfileMultiLineDifferentCoasts(CGeomProfile*, double const, double const); - bool bIncreaseCliffNotchIncision(int const, int const, int const, CRWCliff*, double const); - bool bCreateNotchInland(int const, int const, int const, int const, double const, double const); - - // GIS utility routines - int nMarkBoundingBoxEdgeCells(void); - bool bCheckRasterGISOutputFormat(void); - bool bCheckVectorGISOutputFormat(void); - bool bSaveAllRasterGISFiles(void); - bool bSaveAllVectorGISFiles(void); - bool bIsWithinValidGrid(int const, int const) const; - bool bIsWithinValidGrid(CGeom2DIPoint const*) const; - double dGridCentroidXToExtCRSX(int const) const; - double dGridCentroidYToExtCRSY(int const) const; - double dGridXToExtCRSX(double const) const; - double dGridYToExtCRSY(double const) const; - // double dExtCRSXToGridCentroidX(double const) const; - // double dExtCRSYToGridCentroidY(double const) const; - CGeom2DIPoint PtiExtCRSToGridRound(CGeom2DPoint const*) const; - CGeom2DPoint PtGridCentroidToExt(CGeom2DIPoint const*) const; - double dExtCRSXToGridX(double const) const; - double dExtCRSYToGridY(double const) const; - static double dGetDistanceBetween(CGeom2DPoint const*, CGeom2DPoint const*); - static double dGetDistanceBetween(CGeom2DIPoint const*, CGeom2DIPoint const*); - static double dGetDistanceBetween(double const, double const, double const, double const); - static double dTriangleAreax2(CGeom2DPoint const*, CGeom2DPoint const*, CGeom2DPoint const*); - void KeepWithinValidGrid(int&, int&) const; - void KeepWithinValidGrid(int, int, int&, int&) const; - void KeepWithinValidGrid(CGeom2DIPoint const*, CGeom2DIPoint *) const; - static double dKeepWithin360(double const); - // vector VGetPerpendicular(CGeom2DPoint const*, CGeom2DPoint - // const*, double const, int const); - // static CGeom2DPoint PtGetPerpendicular(CGeom2DPoint const*, CGeom2DPoint const*, double const, int const); - static CGeom2DIPoint PtiGetPerpendicular(CGeom2DIPoint const*, CGeom2DIPoint const*, double const, int const); - static CGeom2DIPoint PtiGetPerpendicular(int const, int const, int const, int const, double const, int const); - static CGeom2DPoint PtAverage(CGeom2DPoint const*, CGeom2DPoint const*); - static CGeom2DPoint PtAverage(vector*); - // static CGeom2DIPoint PtiAverage(CGeom2DIPoint const*, CGeom2DIPoint const*); - // static CGeom2DIPoint PtiAverage(vector*); - static CGeom2DIPoint PtiWeightedAverage(CGeom2DIPoint const*, CGeom2DIPoint const*, double const); - static CGeom2DIPoint PtiPolygonCentroid(vector*); - static double dAngleSubtended(CGeom2DIPoint const*, CGeom2DIPoint const*, CGeom2DIPoint const*); - static int nGetOppositeDirection(int const); - // static void GetSlopeAndInterceptFromPoints(CGeom2DIPoint const*, - // CGeom2DIPoint const*, double&, double&); - CGeom2DIPoint PtiFindClosestCoastPoint(int const, int const, int&); - int nFindClosestCoastPoint(int const, int const, int&); - int nConvertMetresToNumCells(double const) const; - bool bIsAdjacentEdgeCell(CGeom2DIPoint const*, CGeom2DIPoint const*); - void FindClosestPointOnStraightLine(double const, double const, double const, double const, double const, double const, double&, double&); - - // Interpolation routines - double dGetInterpolatedValue(vector const*, vector const*, double, bool); - double dGetInterpolatedValue(vector const*, vector const*, int, bool); - int nFindIndex(vector const*, double const); - vector VdInterpolateCShoreProfileOutput(vector const*, vector const*, vector const*); - - // Utility routines - static void AnnounceStart(void); - void AnnounceLicence(void); - void AnnounceReadBasementDEM(void) const; - static void AnnounceAddLayers(void); - static void AnnounceReadRasterFiles(void); - static void AnnounceReadVectorFiles(void); - void AnnounceReadLGIS(void) const; - void AnnounceReadICGIS(void) const; - void AnnounceReadIHGIS(void) const; - static void AnnounceFinalInitialization(void); - void AnnounceReadInitialSuspSedGIS(void) const; - void AnnounceReadInitialFineUnconsSedGIS(int const) const; - void AnnounceReadInitialSandUnconsSedGIS(int const) const; - void AnnounceReadInitialCoarseUnconsSedGIS(int const) const; - void AnnounceReadInitialFineConsSedGIS(int const) const; - void AnnounceReadInitialSandConsSedGIS(int const) const; - void AnnounceReadInitialCoarseConsSedGIS(int const) const; - void AnnounceReadDeepWaterWaveValuesGIS(void) const; - void AnnounceReadSedimentEventInputValuesGIS(void) const; - void AnnounceReadFloodLocationGIS(void) const; - void AnnounceReadTideData(void) const; - static void AnnounceReadSCAPEShapeFunctionFile(void); - static void AnnounceAllocateMemory(void); - static void AnnounceIsRunning(void); - static void AnnounceSimEnd(void); - void StartClock(void); - bool bFindExeDir(char const*); - bool bTimeToQuit(void); - static int nDoTimeUnits(string const*); - int nDoSimulationTimeMultiplier(string const*); - static double dGetTimeMultiplier(string const*); - static bool bParseDate(string const*, int&, int&, int&); - static bool bParseTime(string const*, int&, int&, int&); - void DoEndOfTimestepTotals(void); - static string strGetBuild(void); - static string strGetComputerName(void); - void DoCPUClockReset(void); - // void CalcTime(double const); - static string strDispTime(double const, bool const, bool const); - static string strDispSimTime(double const); - void AnnounceProgress(void); - static string strGetErrorText(int const); - string strListRasterFiles(void) const; - string strListVectorFiles(void) const; - string strListTSFiles(void) const; - void CalcProcessStats(void); - void CalcSavitzkyGolayCoeffs(void); - CGeomLine LSmoothCoastSavitzkyGolay(CGeomLine *, int const, int const) const; - CGeomLine LSmoothCoastRunningMean(CGeomLine *) const; - vector dVSmoothProfileSlope(vector*) const; - // vector dVCalCGeomProfileSlope(vector*, vector*); // TODO 007 Why was this removed? vector - // dVSmoothProfileSavitzkyGolay(vector*, vector*); // - // TODO 007 was this removed? vector - // dVSmoothProfileRunningMean(vector*); // - // TODO 007 was this removed? - static void CalcSavitzkyGolay(double[], int const, int const, int const, int const, int const); - static string pstrChangeToBackslash(string const*); - static string pstrChangeToForwardSlash(string const*); - static string strTrim(string const*); - static string strTrimLeft(string const*); - static string strTrimRight(string const*); - static string strToLower(string const*); - // static string strToUpper(string const*); - static string strRemoveSubstr(string*, string const*); - static vector* VstrSplit(string const*, char const, vector*); - static vector VstrSplit(string const*, char const); - // static double dCrossProduct(double const, double const, double const, - // double const, double const, double const); static double - // dGetMean(vector const*); static double dGetStdDev(vector - // const*); - static void AppendEnsureNoGap(vector*, CGeom2DIPoint const*); - // static bool bIsNumeric(string const*); - unsigned long ulConvertToTimestep(string const*) const; - void WritePolygonInfoTable(void); - void WritePolygonPreExistingSedimentTable(void); - void WritePolygonSedimentInputEventTable(void); - void WritePolygonShorePlatformErosion(void); - void WritePolygonCliffCollapseErosion(void); - void WritePolygonSedimentBeforeMovement(void); - void WritePolygonPotentialErosion(void); - // void WritePolygonUnconsErosion(int const); - void WritePolygonUnsortedSequence(vector>>&); - void WritePolygonSortedSequence(vector>>&); - void WritePolygonActualMovement(vector>>&); - void DoEndOfRunDeletes(void); - - protected: - public: - ofstream LogStream; - - CSimulation(void); - ~CSimulation(void); - - //! Returns the NODATA value - double dGetMissingValue(void) const; - - //! Returns this timestep's SWL - double dGetThisIterSWL(void) const; - - //! Returns this timestep's total water level TODO 007 Finish surge and runup stuff - double dGetThisIterTotWaterLevel(void) const; - - // //! Returns the vertical tolerance for beach cells to be included in smoothing - // double dGetMaxBeachElevAboveSWL(void) const; - - //! Returns the cell size - // double dGetCellSide(void) const; - - //! Returns the size of the grid in the X direction - int nGetGridXMax(void) const; - - //! Returns the size of the grid in the Y direction - int nGetGridYMax(void) const; - - //! Returns the global d50 value for fine sediment - double dGetD50Fine(void) const; - - //! Returns the global d50 value for sand sediment - double dGetD50Sand(void) const; - - //! Returns the global d50 value for coarse sediment - double dGetD50Coarse(void) const; - - //! Runs the simulation - int nDoSimulation(int, char const*[]); - - //! Carries out end-of-simulation tidying (error messages etc.) - void DoSimulationEnd(int const); -}; -#endif // SIMULATION_H +/*! + \class CSimulation + \brief This class runs CoastalME simulations + \details TODO 001 This is a more detailed description of the CSimulation + class + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License + \file simulation.h + \brief Contains CSimulation definitions +*/ + +#ifndef SIMULATION_H +#define SIMULATION_H +/* =============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +===============================================================================================================================*/ +#include +using std::vector; + +#include +using std::localtime; +using std::time; +using std::time_t; + +#include +using std::ofstream; + +#include +using std::string; + +#include +using std::pair; + +#include +using std::stack; + +#include +using std::default_random_engine; +using std::normal_distribution; + +#include +using ::GDALDataType; + +#include "yaml_parser.h" +#include "configuration.h" +#include "2d_point.h" +#include "2di_point.h" +#include "line.h" +#include "cme.h" +#include "line.h" + +class CGeomRasterGrid; // Forward declarations +class CRWCoast; +class CGeomProfile; +class CGeomCoastPolygon; +class CRWCliff; +class CRWSedInputEvent; +class CRWCellLandform; + +class CSimulation +{ + private: + //! Does this simulation consider fine-sized sediment? + bool m_bHaveFineSediment; + + //! Does this simulation consider sand-sized sediment? + bool m_bHaveSandSediment; + + //! Does this simulation consider coarse-sized sediment? + bool m_bHaveCoarseSediment; + + //! Save basement raster DEMs? + bool m_bBasementElevSave; + + //! Save sediment (inc talus) top surface raster DEMs? + bool m_bSedIncTalusTopSurfSave; + + //! Save top surface (sediment, talus, and sea) raster DEMs? + bool m_bTopSurfIncSeaSave; + + //! Save talus depth? + bool m_bTalusSave; + + //! Save slope raster grids (used for cliff toe location)? + bool m_bSlopeSaveForCliffToe; + + //! Save cliff toe raster grids? + bool m_bCliffToeSave; + + //! Save slices? + bool m_bSliceSave; + + //! Save sea depth raster GIS files? + bool m_bSeaDepthSave; + + //! Save average sea depth raster GIS files? + bool m_bAvgSeaDepthSave; + + //! Save wave height raster GIS files? + bool m_bWaveHeightSave; + + //! Save wave height raster GIS files? + bool m_bAvgWaveHeightSave; + + //! Save wave angle raster GIS files? + bool m_bWaveAngleSave; + + //! Save average wave angle raster GIS files? + bool m_bAvgWaveAngleSave; + + //! Save wave angle and wave height raster GIS files? + bool m_bWaveAngleAndHeightSave; + + //! Save average wave angle and average wave height raster GIS files? + bool m_bAvgWaveAngleAndHeightSave; + + //! Save deep water wave angle and wave height raster GIS files? + bool m_bDeepWaterWaveAngleAndHeightSave; + + //! Save wave energy since cliff collapse raster GIS files? + bool m_bWaveEnergySinceCollapseSave; + + //! Save mean wave energy raster GIS files? + bool m_bMeanWaveEnergySave; + + //! Save breaking wave height raster GIS files? + bool m_bBreakingWaveHeightSave; + + //! Save beach protection raster GIS files> + bool m_bBeachProtectionSave; + + //! Save potential shore platform erosion raster GIS files? + bool m_bPotentialPlatformErosionSave; + + //! Save actual (supply-limited) shore platform erosion raster GIS files? + bool m_bActualPlatformErosionSave; + + //! Save total potential shore platform erosion raster GIS files? + bool m_bTotalPotentialPlatformErosionSave; + + //! Save total actual (supply-limited) shore platform erosion raster GIS files? + bool m_bTotalActualPlatformErosionSave; + + //! Save potential beach (unconsolidated sediment) erosion raster GIS files? + bool m_bPotentialBeachErosionSave; + + //! Save actual (supply-limited) beach (unconsolidated sediment) erosion raster GIS files? + bool m_bActualBeachErosionSave; + + //! Save total potential beach (unconsolidated sediment) erosion raster GIS files? + bool m_bTotalPotentialBeachErosionSave; + + //! Save total actual (supply-limited) beach (unconsolidated sediment) erosion raster GIS files? + bool m_bTotalActualBeachErosionSave; + + //! Save beach (unconsolidated sediment) deposition raster GIS files? + bool m_bBeachDepositionSave; + + //! Save total beach (unconsolidated sediment) deposition raster GIS files? + bool m_bTotalBeachDepositionSave; + + //! Save coast landform raster GIS files? + bool m_bLandformSave; + + //! Save slope of consolidated sediment raster GIS files? + bool m_bSlopeConsSedSave; + + //! Save intervention class raster GIS files? + bool m_bInterventionClassSave; + + //! Save intervention height raster GIS files? + bool m_bInterventionHeightSave; + + //! Save suspended sediment raster GIS files? + bool m_bSuspSedSave; + + //! Save average suspended sediment raster GIS files? + bool m_bAvgSuspSedSave; + + //! Save fine unconsolidated sediment raster GIS files? + bool m_bFineUnconsSedSave; + + //! Save sand unconsolidated sediment raster GIS files? + bool m_bSandUnconsSedSave; + + //! Save coarse unconsolidated sediment raster GIS files? + bool m_bCoarseUnconsSedSave; + + //! Save fine consolidated sediment raster GIS files? + bool m_bFineConsSedSave; + + //! Save sand consolidated sediment raster GIS files? + bool m_bSandConsSedSave; + + //! Save coarse consolidated sediment raster GIS files? + bool m_bCoarseConsSedSave; + + //! Save rasterized coastline GIS files? + bool m_bRasterCoastlineSave; + + //! Save rasterized coastline-normal profiles GIS files? + bool m_bRasterNormalProfileSave; + + //! Save active zone raster GIS files? + bool m_bActiveZoneSave; + + //! Save cliff collapse raster GIS files? + bool m_bCliffCollapseSave; + + //! Save total cliff collapse raster GIS files? + bool m_bTotCliffCollapseSave; + + //! Save cliff collapse deposition raster GIS files? + bool m_bCliffCollapseDepositionSave; + + //! Save total cliff collapse deposition raster GIS files? + bool m_bTotCliffCollapseDepositionSave; + + //! Save raster polygon raster GIS files? + bool m_bRasterPolygonSave; + + //! Save potential platform erosion mask raster GIS files? + bool m_bPotentialPlatformErosionMaskSave; + + //! Save sea mask raster GIS files? + bool m_bSeaMaskSave; + + //! Save beach mask raster GIS files? + bool m_bBeachMaskSave; + + //! Save wave shadow zones raster GIS files? + bool m_bShadowZoneCodesSave; + + //! Save deep water wave angle raster GIS files? + bool m_bDeepWaterWaveAngleSave; + + //! Save deep water wave height raster GIS files? + bool m_bDeepWaterWaveHeightSave; + + //! Save deep water wave period raster GIS files? + bool m_bDeepWaterWavePeriodSave; + + //! Save polygon unconsolidated sediment up- or down-drift raster GIS files? + bool m_bPolygonUnconsSedUpOrDownDriftSave; + + //! Save polygon unconsolidated sediment gain or loss raster GIS files? + bool m_bPolygonUnconsSedGainOrLossSave; + + //! Save GIS files at regular intervals? + bool m_bSaveRegular; + + //! Save coastline as vector GIS file? + bool m_bCoastSave; + + //! Save cliff edge vector GIS files? + bool m_bCliffEdgeSave; + + //! Save coastline-normal vector GIS files? + bool m_bNormalsSave; + + //! Save invalid coastline-normal vector GIS files? + bool m_bInvalidNormalsSave; + + //! Save coastline-curvature vector GIS files? + bool m_bCoastCurvatureSave; + + //! Save polygon node vector GIS files? + bool m_bPolygonNodeSave; + + //! Save polygon boundary vector GIS files? + bool m_bPolygonBoundarySave; + + //! Save cliff notch incision depth vector GIS files? + bool m_bCliffNotchSave; + + //! Save wave transect points vector GIS files? + bool m_bWaveTransectPointsSave; + + //! Save wave shadow boundary vector GIS files? + bool m_bShadowBoundarySave; + + //! Save wave shadow downdrift boundary vector GIS files? + bool m_bShadowDowndriftBoundarySave; + + //! Save the sea area time series file? + bool m_bSeaAreaTSSave; + + //! Save the SWL (still water level) time series file? + bool m_bSWLTSSave; + + //! Save the actual (supply-limited) shore platform erosion time series file? + bool m_bActualPlatformErosionTSSave; + + //! Save the cliff collapse erosion time series file? + bool m_bCliffCollapseErosionTSSave; + + //! Save the cliff collapse deposition time series file? + bool m_bCliffCollapseDepositionTSSave; + + //! Save the cliff collapse net change time series file? + bool m_bCliffCollapseNetTSSave; + + //! Save the beach (unconsolidated sediment) erosion time series file? + bool m_bBeachErosionTSSave; + + //! Save the beach (unconsolidated sediment) deposition time series file? + bool m_bBeachDepositionTSSave; + + //! Save the beach (unconsolidated sediment) net change time series file? + bool m_bBeachSedimentChangeNetTSSave; + + //! Save the suspended sediment time series file? + bool m_bSuspSedTSSave; + + //! Save the flood setup surge time series file? TODO 007 Finish surge and runup stuff + bool m_bFloodSetupSurgeTSSave; + + //! Save the flood setup surge runup time series file? TODO 007 Finish surge and runup stuff + bool m_bFloodSetupSurgeRunupTSSave; + + //! Save the cliff notch elevation time series file? + bool m_bCliffNotchElevTSSave; + + //! Save GIS files this iteration? + bool m_bSaveGISThisIter; + + //! Output profile data? + bool m_bOutputConsolidatedProfileData; + + //! Output parallel profile data? + bool m_bOutputParallelProfileData; + + //! Output erosion potential data? + bool m_bOutputErosionPotentialData; + + //! Output per-timestep results in CSV format instead of fixed-width? + bool m_bCSVPerTimestepResults; + + //! Use YAML format for input datafile instead of .dat format? + bool m_bYamlInputFormat; + + //! Omit the north edge of the grid from coast-end searches? + bool m_bOmitSearchNorthEdge; + + //! Omit the south edge of the grid from coast-end searches? + bool m_bOmitSearchSouthEdge; + + //! Omit the west edge of the grid from coast-end searches? + bool m_bOmitSearchWestEdge; + + //! Omit the east edge of the grid from coast-end searches? + bool m_bOmitSearchEastEdge; + + //! Simulate shore platform erosion? + bool m_bDoShorePlatformErosion; + + //! Simulate cliff collapse? + bool m_bDoCliffCollapse; + + //! Simulate unconsolidated sediment (beach) transport? + bool m_bDoBeachSedimentTransport; + + //! Is the selected GDAL output file format capable of writing files? + bool m_bGDALCanCreate; + + //! Is the selected GDAL output file format capable of writing floating-point values to files? + bool m_bGDALCanWriteFloat; + + //! Is the selected GDAL output file format capable of writing 32-bit integers to files? + bool m_bGDALCanWriteInt32; + + //! Scale raster output? + bool m_bScaleRasterOutput; + + //! Write a GIS World file? + bool m_bWorldFile; + + //! Do we have just a point source for (i.e. only a single measurement of) deep water wave values + bool m_bSingleDeepWaterWaveValues; + + //! Do we have wave station data? + bool m_bHaveWaveStationData; + + //! Do we have sediment input events? + bool m_bSedimentInput; + + //! Do we have sediment inputat a point? + bool m_bSedimentInputAtPoint; + + //! Do we have sediment input at the coast? + bool m_bSedimentInputAtCoast; + + //! Do we have sediment input along a line? + bool m_bSedimentInputAlongLine; + + //! Save sediment inut data? + bool m_bSedimentInputEventSave; + + //! Do we have a sediment input event this iteration? + bool m_bSedimentInputThisIter; + + //! Are we doing riverine flooding? + bool m_bRiverineFlooding; + + //! Are we saving the wave setup? TODO 007 Finish surge and runup stuff + bool m_bWaveSetupSave; + + //! Are we saving the storm surge? TODO 007 Finish surge and runup stuff + bool m_bStormSurgeSave; + + //! Are we saving runup? TODO 007 Finish surge and runup stuff + bool m_bRunUpSave; + + //! Are we saving the setup surge flood mask? TODO 007 Finish surge and runup stuff + bool m_bSetupSurgeFloodMaskSave; + + //! Are we saving the setup surge runup flood mask? TODO 007 Finish surge and runup stuff + bool m_bSetupSurgeRunupFloodMaskSave; + + //! Are we saving the raster wave flood line? TODO 007 Finish surge and runup stuff + bool m_bRasterWaveFloodLineSave; + + //! Are we saving the vector wave flood line? TODO 007 Finish surge and runup stuff + bool m_bVectorWaveFloodLineSave; + + //! Are we saving the flood location? TODO 007 Finish surge and runup stuff + bool m_bFloodLocationSave; + + //! Are we saving the flood still water level setup line? TODO 007 Finish surge and runup stuff + bool m_bFloodSWLSetupLineSave; + + //! Are we saving the flood still water level setup surge line? TODO 007 Finish surge and runup stuff + bool m_bFloodSWLSetupSurgeLine; + + //! Are we saving all cliff notches? + bool m_bCliffNotchAllSave; + + //! Are we saving the timestep at which each cliff occurred? + bool m_bCliffCollapseTimestepSave; + + //! Are we saving the flood still water level setup surge runup line? TODO 007 Finish surge and runup stuff + bool m_bFloodSWLSetupSurgeRunupLineSave; + + //! Are the GIS save digits (which are part of each GIS file name) sequential, or are they the iteration number? + bool m_bGISSaveDigitsSequential; + + //! Does this simulation consider consolidated sediment, or is it an unconsolidated sediment only simulation?> + bool m_bHaveConsolidatedSediment; + + //! GDAL optimisations enabled? + bool m_bGDALOptimisations; + + //! Cliff to location? + bool m_bCliffToeLocate; + + //! Do we have the highest SWL so far? + bool m_bHighestSWLSoFar; + + //! Do we have the lowest SWL so far? + bool m_bLowestSWLSoFar; + + //! Options for GDAL when handling raster files + char **m_papszGDALRasterOptions; + + //! Options for GDAL when handling vector files + char **m_papszGDALVectorOptions; + + //! The size of the grid in the x direction + int m_nXGridSize; + + //! The size of the grid in the y direction + int m_nYGridSize; + + //! The number of sediment layers + int m_nLayers; + + //! Which method to use for coast smoothing + int m_nCoastSmooth; + + //! The size of the window used for coast smoothing. Must be an odd number + int m_nCoastSmoothingWindowSize; + + //! The order of the coastline profile smoothing polynomial if Savitzky-Golay smoothing is used (usually 2 or 4, max is 6) + int m_nSavGolCoastPoly; + + //! Which method to use for cliff edge smoothing + int m_nCliffEdgeSmooth; + + //! The size of the window used for cliff edge smoothing. Must be an odd number + int m_nCliffEdgeSmoothWindow; + + //! The order of the cliff edge smoothing polynomial if Savitzky-Golay smoothing is used (usually 2 or 4, max is 6) + int m_nSavGolCliffEdgePoly; + + //! The size of the window used for running-mean coast-normal profile smoothing (must be odd) + int m_nProfileSmoothWindow; + + //! Average spacing between coastline normals, measured in cells + int m_nCoastNormalSpacing; + + //! Average spacing between coastline normals on interventions, measured in cells + int m_nCoastNormalInterventionSpacing; + + //! Coast curvature interval is a length, measured in coastline points + int m_nCoastCurvatureInterval; + + //! The maximum number of digits in GIS filenames. These can be sequential, or the iteration number + int m_nGISMaxSaveDigits; + + //! The save number for GIS files (can be sequential, or the iteration number) + int m_nGISSave; + + //! If user-defined GIS save intervals, the number of these + int m_nUSave; + + //! Used in calculations of GIS save intervals + int m_nThisSave; + + //! Maximum valid coast length when searching for coasts + int m_nCoastMax; + + //! Minimum valid coast length when searching for coasts + int m_nCoastMin; + + //! The number of cells with cliff collapse this iteration + int m_nNumThisIterCliffCollapse; + + //! The total number of cells with cliff collapse since the start of the simulation + int m_nNumTotCliffCollapse; + + //! How sediment which moves off an edge of the grid is handled. Possible values are GRID_EDGE_CLOSED, GRID_EDGE_OPEN, GRID_EDGE_RECIRCULATE + int m_nUnconsSedimentHandlingAtGridEdges; + + //! Which beach erosion-deposition equation is used. Possible values are UNCONS_SEDIMENT_EQUATION_CERC and UNCONS_SEDIMENT_EQUATION_KAMPHUIS + int m_nBeachErosionDepositionEquation; + + //! The value for integer missing values, as read from GIS input files + int m_nGISMissingValue; + + //! Used by CoastalME for integer missing values + int m_nMissingValue; + + //! The minimum x value of the bounding box + int m_nXMinBoundingBox; + + //! The maximum x value of the bounding box + int m_nXMaxBoundingBox; + + //! The minimum y value of the bounding box + int m_nYMinBoundingBox; + + //! The maximum y value of the bounding box + int m_nYMaxBoundingBox; + + //! The wave propagation model used. Possible values are WAVE_MODEL_CSHORE and WAVE_MODEL_COVE + int m_nWavePropagationModel; + + //! Start time of the simulation (seconds) + int m_nSimStartSec; + + //! Start time of the simulation (minutes) + int m_nSimStartMin; + + //! Start time of the simulation (hours) + int m_nSimStartHour; + + //! Start date of the simulation (day) + int m_nSimStartDay; + + //! Start date of the simulation (month) + int m_nSimStartMonth; + + //! Start date of the simulation (year) + int m_nSimStartYear; + + //! The duration of data for deep water waves, expressed as a number of timesteps + int m_nDeepWaterWaveDataNumTimeSteps; + + //! The level of detail in the log file output. Can be LOG_FILE_LOW_DETAIL, LOG_FILE_MIDDLE_DETAIL, LOG_FILE_HIGH_DETAIL, or LOG_FILE_ALL + int m_nLogFileDetail; + + //! The run-up equation used TODO 007 Finish surge and runup stuff + int m_nRunUpEquation; + + //! TODO 007 Used in WAVESETUP + SURGE + RUNUP Finish surge and runup stuff + int m_nLevel; + + //! The data type used by GDAL for integer operations, can be GDT_Byte, GDT_Int16, GDT_UInt16, GDT_Int32, or GDT_UInt32 + GDALDataType m_GDALWriteIntDataType; + + //! Thw data type used by GDAL for floating point operations, can be GDT_Byte, GDT_Int16, GDT_UInt16, GDT_Int32, GDT_UInt32, or GDT_Float32 + GDALDataType m_GDALWriteFloatDataType; + + //! The maximum integer value which GDAL can write, can be UINT8_MAX, INT16_MAX, UINT16_MAX, INT32_MAX, or UINT32_MAX, + long m_lGDALMaxCanWrite; + + //! The minimum integer value which GDAL can write, can be zero, INT16_MIN, INT32_MIN + long m_lGDALMinCanWrite; + + //! The number of the current iteration (time step) + unsigned long m_ulIter; + + //! The target number of iterations + unsigned long m_ulTotTimestep; + + //! A seed for each of the random number generators + unsigned long m_ulRandSeed[NUMBER_OF_RNGS]; + + //! The number of cells in the grid + unsigned long m_ulNumCells; + + //! The number of grid cells which are marked as sea, for this iteration + unsigned long m_ulThisIterNumSeaCells; + + //! The number of grid cells which are marked as coast, for this iteration + unsigned long m_ulThisIterNumCoastCells; + + //! The number of grid cells on which potential platform erosion occurs, for this iteration + unsigned long m_ulThisIterNumPotentialPlatformErosionCells; + + //! The number of grid cells on which actual platform erosion occurs, for this iteration + unsigned long m_ulThisIterNumActualPlatformErosionCells; + + //! The number of grid cells on which potential beach (unconsolidated sediment) erosion occurs, for this iteration + unsigned long m_ulThisIterNumPotentialBeachErosionCells; + + //! The number of grid cells on which actual beach (unconsolidated sediment) erosion occurs, for this iteration + unsigned long m_ulThisIterNumActualBeachErosionCells; + + //! The number of grid cells on which beach (unconsolidated sediment) deposition occurs, for this iteration + unsigned long m_ulThisIterNumBeachDepositionCells; + + //! The number of cells on which on-profile average potential shore platform erosion occurs + unsigned long m_ulTotPotentialPlatformErosionOnProfiles; + + //! The number of cells on which between-profile average potential shore platform erosion occurs + unsigned long m_ulTotPotentialPlatformErosionBetweenProfiles; + + //! The number of basement cells marked with as missing value + unsigned long m_ulMissingValueBasementCells; + + //! Used by CoastalME for unsigned long integer missing values + unsigned long m_ulMissingValue; + + //! Multiplier for duration units, to convert to hours + double m_dDurationUnitsMult; + + //! The north-west x coordinate, in the external coordinate reference system (CRS) + double m_dNorthWestXExtCRS; + + //! The north-west y coordinate, in the external coordinate reference system (CRS) + double m_dNorthWestYExtCRS; + + //! The south-east x coordinate, in the external coordinate reference system (CRS) + double m_dSouthEastXExtCRS; + + //! The south-east y coordinate, in the external coordinate reference system (CRS) + double m_dSouthEastYExtCRS; + + //! The area of the grid (in external CRS units) + double m_dExtCRSGridArea; + + //! Length of a cell side (in external CRS units) + double m_dCellSide; + + //! Area of a cell (in external CRS units) + double m_dCellArea; + + //! Length of a cell's diagonal (in external CRS units) + double m_dCellDiagonal; + + //! Inverse of m_dCellSide + double m_dInvCellSide; + + //! Inverse of m_dCellDiagonal + double m_dInvCellDiagonal; + + //! Duration of simulation, in hours + double m_dSimDuration; + + //! The length of an iteration (a timestep) in hours + double m_dTimeStep; + + //! Time simulated so far, in hours + double m_dSimElapsed; + + //! The time of the next save, in hours from the start of the simulation, if we are saving regularly + double m_dRegularSaveTime; + + //! The interval between regular saves, in hours + double m_dRegularSaveInterval; + + //! Save time, in hours from the start of the simukation, if we are not saving regularly + double m_dUSaveTime[SAVEMAX]; + + //! Last value returned by clock() + double m_dClkLast; + + //! Total elapsed CPU time + double m_dCPUClock; + + //! GDAL geotransformation info (see http://www.gdal.org/classGDALDataset.html) + double m_dGeoTransform[6]; + + //! Density of sea water in kg/m**3 + double m_dSeaWaterDensity; + + //! The start-of-simulation still water level (m) + double m_dInitialMeanSWL; + + //! The end-of-simulation still water (m), is same as m_dInitialMeanSWL unless SWL changes + double m_dFinalMeanSWL; + + //! If long-term SWL changes, the increment per timestep + double m_dDeltaSWLPerTimestep; + + //! The still water level for this timestep (this includes tidal changes and any long-term SWL change) + double m_dThisIterSWL; + + //! The mean still water level for this timestep (does not include tidal changes, but includes any long-term SWL change) + double m_dThisIterMeanSWL; + + //! If long-term SWL changes, the total change (m) so far since the start of simulation + double m_dAccumulatedSeaLevelChange; + + //! Minimum still water level (m) + double m_dMinSWLSoFar; + + //! Maximum still water level (m) + double m_dMaxSWLSoFar; + + //! TODO 007 Finish surge and runup stuff + double m_dThisIterDiffTotWaterLevel; + + //! TODO 007 Finish surge and runup stuff + double m_dThisIterDiffWaveSetupWaterLevel; + + //! TODO 007 Finish surge and runup stuff + double m_dThisIterDiffWaveSetupSurgeWaterLevel; + + //! TODO 007 Finish surge and runup stuff + double m_dThisIterDiffWaveSetupSurgeRunupWaterLevel; + + //! The height of breaking waves (m) + double m_dBreakingWaveHeight; + + //! Deep water wave speed (m/s) + double m_dC_0; + + //! Deep water wave length (m) + double m_dL_0; + + //! Start depth for wave calculations + double m_dWaveDepthRatioForWaveCalcs; + + //! Breaking wave height-to-depth ratio + double m_dBreakingWaveHeightDepthRatio; + + //! Deep water wave height (m) for all sea cells + double m_dAllCellsDeepWaterWaveHeight; + + //! Deep water wave angle for all sea cells + double m_dAllCellsDeepWaterWaveAngle; + + //! Deep water wave period for all sea cells + double m_dAllCellsDeepWaterWavePeriod; + + //! Maximum deep water wave height + double m_dMaxUserInputWaveHeight; + + //! Used to constrain depth of closure + double m_dMaxUserInputWavePeriod; + + //! Coast platform resistance to erosion R, see Walkden & Hall, 2011 + double m_dR; + + //! The D50 for fine sediment + double m_dD50Fine; + + //! The D50 for sand sediment + double m_dD50Sand; + + //! The D50 for coarse sediment + double m_dD50Coarse; + + //! The density of unconsolidated beach sediment (kg/m**3) + double m_dBeachSedimentDensity; + + //! The porosity of unconsolidated beach sediment (0-1) + double m_dBeachSedimentPorosity; + + //! The relative erodibility (0-1) of fine unconsolidated beach sediment + double m_dFineErodibility; + + //! The relative erodibility (0-1) of sand unconsolidated beach sediment + double m_dSandErodibility; + + //! The relative erodibility (0-1) of coarse unconsolidated beach sediment + double m_dCoarseErodibility; + + //! Relative erodibility of fine unconsolidated beach sediment, normalized + double m_dFineErodibilityNormalized; + + //! Relative erodibility of sand unconsolidated beach sediment, normalized + double m_dSandErodibilityNormalized; + + //! Relative erodibility of coarse unconsolidated beach sediment, normalized + double m_dCoarseErodibilityNormalized; + + //! Transport parameter KLS in the CERC equation + double m_dKLS; + + //! Transport parameter for the Kamphuis equation + double m_dKamphuis; + + //! Gravitational acceleration (m**2/sec) + double m_dG; + + //! For beach erosion/deposition, conversion from immersed weight to bulk volumetric (sand and voids) transport rate (Leo Van Rijn) TODO 007 Need date of reference + double m_dInmersedToBulkVolumetric; + + //! Depth of closure (in m) TODO 007 can be calculated using Hallermeier, R.J. (1978) or Birkemeier (1985) TODO 045 This needs to be a user decision + double m_dDepthOfClosure; + + //! Average spacing of the coastline-normal profiles, in m + double m_dCoastNormalSpacing; + + //! Average spacing of the coastline-normal profiles on interventions, in m + double m_dCoastNormalInterventionSpacing; + + //! Length of the coastline-normal profiles, in m + double m_dCoastNormalLength; + + //! Approximate minimum spacing (m) between wave transects (real and synthetic) for wave interpolation densification + double m_dSyntheticTransectSpacing; + + //! Total sea depth (m) for this iteration + double m_dThisIterTotSeaDepth; + + //! Total potential platform erosion (all size classes of consolidated sediment) for this iteration (depth in m) + double m_dThisIterPotentialPlatformErosion; + + //! Total actual platform erosion (fine consolidated sediment) for this iteration (depth in m) + double m_dThisIterActualPlatformErosionFineCons; + + //! Total actual platform erosion (sand consolidated sediment) for this iteration (depth in m) + double m_dThisIterActualPlatformErosionSandCons; + + //! Total actual platform erosion (coarse consolidated sediment) for this iteration (depth in m) + double m_dThisIterActualPlatformErosionCoarseCons; + + //! Total potential beach erosion (all size classes of unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterPotentialBeachErosion; + + //! Total actual beach erosion (fine unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterBeachErosionFine; + + //! Total actual beach erosion (sand unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterBeachErosionSand; + + //! Total actual beach erosion (coarse unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterBeachErosionCoarse; + + //! Total beach deposition (sand unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterBeachDepositionSand; + + //! Total beach deposition (coarse unconsolidated sediment) for this iteration (depth in m) + double m_dThisIterBeachDepositionCoarse; + + //! Total fine unconsolidated sediment in suspension for this iteration (depth in m) + double m_dThisIterFineSedimentToSuspension; + + //! Total unconsolidated sediment from beach erosion (all size classes) lost from the grid this iteration (depth in m) + double m_dThisIterPotentialSedLostBeachErosion; + + //! Total fine unconsolidated sediment lost from the grid this iteration (depth in m) + double m_dThisIterLeftGridUnconsFine; + + //! Total sand unconsolidated sediment lost from the grid this iteration (depth in m) + double m_dThisIterLeftGridUnconsSand; + + //! Total coarse unconsolidated sediment lost from the grid this iteration (depth in m) + double m_dThisIterLeftGridUnconsCoarse; + + //! Total fine sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) + double m_dThisIterCliffCollapseFineErodedDuringDeposition; + + //! Total sand sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) + double m_dThisIterCliffCollapseSandErodedDuringDeposition; + + //! Total coarse sediment eroded during Dean profile deposition of talus following cliff collapse (depth in m) + double m_dThisIterCliffCollapseCoarseErodedDuringDeposition; + + //! Error term: if we are unable to deposit enough unconslidated sand on polygon(s), this is held over to be deposited the next iteration + double m_dDepositionSandDiff; + + //! Error term: if we are unable to deposit enough unconslidated coarse on polygon(s), this is held over to be deposited the next iteration + double m_dDepositionCoarseDiff; + + //! Maximum value of deoth over DB, is used in erosion potential look-up function + double m_dDepthOverDBMax; + + //! Total potential platform erosion on profiles + double m_dTotPotentialPlatformErosionOnProfiles; + + //! Total potential platform erosion between profiles + double m_dTotPotentialPlatformErosionBetweenProfiles; + + //! Maximum slope on coastline-normal profiles + double m_dProfileMaxSlope; + + //! Maximum elevation of beach above SWL (m) + double m_dMaxBeachElevAboveSWL; + + //! Resistance of cliff to notch erosion + double m_dCliffErosionResistance; + + //! Notch overhang (i.e. length of horizontal incision at the apex elevation) to initiate collapse (m) + double m_dNotchIncisionAtCollapse; + + //! Elevation (m) of the apex of any cliff notches created during this iteration + double m_dThisIterNewNotchApexElev; + + //! Distance of notch base below SWL (m) + double m_dNotchApexAboveMHW; + + //! Scale parameter A for cliff deposition (m^(1/3)), may be zero for auto-calculation + double m_dCliffDepositionA; + + //! Planview width of cliff collapse talus (m) + double m_dCliffDepositionPlanviewWidth; + + //! Planview length of cliff deposition talus (m) + double m_dCliffTalusMinDepositionLength; + + //! Minimum height of the landward end of cliff collapse talus, as a fraction of cliff elevation + double m_dMinCliffTalusHeightFrac; + + //! This-iteration total of fine unconsolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionFineUncons; + + //! This-iteration total of sand unconsolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionSandUncons; + + //! This-iteration total of coarse unconsolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionCoarseUncons; + + //! This-iteration total of fine consolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionFineCons; + + //! This-iteration total of sand consolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionSandCons; + + //! This-iteration total of coarse consolidated sediment produced by cliff collapse (m^3) + double m_dThisIterCliffCollapseErosionCoarseCons; + + //! This-iteration total of sand unconsolidated sediment deposited due to cliff collapse (m^3) + double m_dThisIterUnconsSandCliffDeposition; + + //! This-iteration total of coarse unconsolidated sediment deposited due to cliff collapse (m^3) + double m_dThisIterUnconsCoarseCliffDeposition; + + //! Random factor for spacing of along-coast normals + double m_dCoastNormalRandSpacingFactor; + + //! Berm height i.e. height above SWL of start of depositional Dean profile + double m_dDeanProfileStartAboveSWL; + + //! The value for floating-point missing values, as read from GIS input files + double m_dGISMissingValue; + + //! Used by CoastalME for floating-point missing values + double m_dMissingValue; + + //! Number of hours after which deep water wave data wraps + double m_dWaveDataWrapHours; + + //! This-iteration highest elevation of DEM + double m_dThisIterTopElevMax; + + //! This-iteration lowest elevation of DEM + double m_dThisIterTopElevMin; + + //! Depth (m) of fine unconsolidated sediment added, at this iteration + double m_dThisiterUnconsFineInput; + + //! Depth (m) of sand unconsolidated sediment added, at this iteration + double m_dThisiterUnconsSandInput; + + //! Depth (m) of coarse unconsolidated sediment added, at this iteration + double m_dThisiterUnconsCoarseInput; + + //! Depth (m) of fine suspended sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterSuspFineAllCells; + + //! Depth (m) of fine suspended sediment at the start of the simulation (only cells in polygons) + double m_dStartIterSuspFineInPolygons; + + //! Depth (m) of fine unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterUnconsFineAllCells; + + //! Depth (m) of sand unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterUnconsSandAllCells; + + //! Depth (m) of coarse unconsolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterUnconsCoarseAllCells; + + //! Depth (m) of fine consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterConsFineAllCells; + + //! Depth (m) of sand consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterConsSandAllCells; + + //! Depth (m) of coarse consolidated sediment at the start of the simulation, all cells (both inside and outside polygons) + double m_dStartIterConsCoarseAllCells; + + //! Total fine unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalFineUnconsInPolygons; + + //! Total sand unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalSandUnconsInPolygons; + + //! Total coarse unconsolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalCoarseUnconsInPolygons; + + //! Total fine consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalFineConsInPolygons; + + //! Total sand consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalSandConsInPolygons; + + //! Total coarse consolidated sediment in all polygons, before polygon-to-polygon movement (only cells in polygons) + double m_dTotalCoarseConsInPolygons; + + //! Depth of unconsolidated sand sediment that could not be deposited during the last iteration, carried forward to this iteration + double m_dUnconsSandNotDepositedLastIter; + + //! Depth of unconsolidated coarse sediment that could not be deposited during the last iteration, carried forward to this iteration + double m_dUnconsCoarseNotDepositedLastIter; + + //! This iteration's Mean High Water (MHW) elevation, calculated using a moving time window. If we have no tide data, this is set to mean SWL + double m_dThisIterMHWElev; + + //! Slope limit for cliff toe detection + double m_dSlopeThresholdForCliffToe; + + // These grand totals are all long doubles. The aim is to minimize rounding errors when many very small numbers are added to a single much larger number, see e.g. http://www.ddj.com/cpp/184403224 + //! All-simulation total of potential platform erosion (m), all size classes + long double m_ldGTotPotentialPlatformErosion; + + //! All-simulation total of fine sediment actual platform erosion (m) + long double m_ldGTotFineActualPlatformErosion; + + //! All-simulation total of sand sediment actual platform erosion (m) + long double m_ldGTotSandActualPlatformErosion; + + //! All-simulation total of coarse sediment actual platform erosion (m) + long double m_ldGTotCoarseActualPlatformErosion; + + //! All-simulation total of potential sediment lost via beach (unconsolidated) sediment movement (m), all size classes + long double m_ldGTotPotentialSedLostBeachErosion; + + //! All-simulation total of fine sediment lost via beach (unconsolidated) sediment movement (m) + long double m_ldGTotActualFineLostBeachErosion; + + //! All-simulation total of sand sediment lost via beach (unconsolidated) sediment movement (m) + long double m_ldGTotActualSandLostBeachErosion; + + //! All-simulation total of coarse sediment lost via beach (unconsolidated) sediment movement (m) + long double m_ldGTotActualCoarseLostBeachErosion; + + //! All-simulation total of sand sediment lost via cliff collapse (m) + long double m_ldGTotSandSedLostCliffCollapse; + + //! All-simulation total of coarse sediment lost via cliff collapse (m) + long double m_ldGTotCoarseSedLostCliffCollapse; + + //! All-simulation total of fine sediment from cliff collapse (m) + long double m_ldGTotCliffCollapseFine; + + //! All-simulation total of sand sediment from cliff collapse (m) + long double m_ldGTotCliffCollapseSand; + + //! All-simulation total of coarse sediment from cliff collapse (m) + long double m_ldGTotCliffCollapseCoarse; + + //! All-simulation total of fine sediment moved to suspension, due to cliff collapse (m) + long double m_ldGTotCliffTalusFineToSuspension; + + //! All-simulation total of sand sediment deposited as talus following cliff collapse (m) + long double m_ldGTotCliffTalusSandDeposition; + + //! All-simulation total of coarse sediment deposited as talus following cliff collapse (m) + long double m_ldGTotCliffTalusCoarseDeposition; + + //! All-simulation total of fine sediment eroded during talus deposition following cliff collapse (m) + long double m_ldGTotCliffCollapseFineErodedDuringDeposition; + + //! All-simulation total of sand sediment eroded during talus deposition following cliff collapse (m) + long double m_ldGTotCliffCollapseSandErodedDuringDeposition; + + //! All-simulation total of coarse sediment eroded during talus deposition following cliff collapse (m) + long double m_ldGTotCliffCollapseCoarseErodedDuringDeposition; + + //! All-simulation total of potential beach erosion (m), all size classes + long double m_ldGTotPotentialBeachErosion; + + //! All-simulation total of fine sediment eroded during beach (unconsolidated sediment) movement (m) + long double m_ldGTotActualFineBeachErosion; + + //! All-simulation total of sand sediment eroded during beach (unconsolidated sediment) movement (m) + long double m_ldGTotActualSandBeachErosion; + + //! All-simulation total of coarse sediment eroded during beach (unconsolidated sediment) movement (m) + long double m_ldGTotActualCoarseBeachErosion; + + //! All-simulation total of sand sediment deposited during beach (unconsolidated sediment) movement (m) + long double m_ldGTotSandBeachDeposition; + + //! All-simulation total of coarse sediment deposited during beach (unconsolidated sediment) movement (m) + long double m_ldGTotCoarseBeachDeposition; + + //! All-simulation total of suspended sediment (m) + long double m_ldGTotSuspendedSediment; + + //! All-simulation total of shortfall in unconsolidated sand sediment deposition (m, not currently used) + long double m_ldGTotSandDepositionDiff; + + //! All-simulation total of shortfall in unconsolidated coarse sediment deposition (m, not currently used) + long double m_ldGTotCoarseDepositionDiff; + + //! All-simulation total of fine sediment input (m) + long double m_ldGTotFineSedimentInput; + + //! All-simulation total of sand sediment input (m) + long double m_ldGTotSandSedimentInput; + + //! All-simulation total of coarse sediment input (m) + long double m_ldGTotCoarseSedimentInput; + + //! The CME folder + string m_strCMEDir; + + //! Folder for the CME .ini file + string m_strCMEIni; + + //! An email addresx to which to send end-of-simulation messages + string m_strMailAddress; + + //! Folder in which the CME data file is found + string m_strDataPathName; + + //! Raster GIS output format + string m_strRasterGISOutFormat; + + //! Vector GIS output format + string m_strVectorGISOutFormat; + + //! Name of initial basement DEM file + string m_strInitialBasementDEMFile; + + //! Name of initial landform file + string m_strInitialLandformFile; + + //! Name of intervention class file + string m_strInterventionClassFile; + + //! Name of intervention height file + string m_strInterventionHeightFile; + + //! Name of initial suspended sediment file + string m_strInitialSuspSedimentFile; + + //! Name of SCAPE shape function file + string m_strSCAPEShapeFunctionFile; + + //! Name of tide data file + string m_strTideDataFile; + + //! Name of output log file + string m_strLogFile; + + //! Path for all output files + string m_strOutPath; + + //! Name of main output file + string m_strOutFile; + + //! GDAL code for the basement DEM raster file type + string m_strGDALBasementDEMDriverCode; + + //! GDAL description of the basement DEM raster file type + string m_strGDALBasementDEMDriverDesc; + + //! GDAL projection string for the basement DEM raster file + string m_strGDALBasementDEMProjection; + + //! GDAL data type for the basement DEM raster file + string m_strGDALBasementDEMDataType; + + //! GDAL code for the for the initial landform class raster file + string m_strGDALLDriverCode; + + //! GDAL description of the initial landform class raster file + string m_strGDALLDriverDesc; + + //! GDAL projection string for the initial landform class raster file + string m_strGDALLProjection; + + //! GDAL data type for the initial landform class raster file + string m_strGDALLDataType; + + //! GDAL code for the initial intervention class raster file + string m_strGDALICDriverCode; + + //! GDAL description of the initial intervention class raster file + string m_strGDALICDriverDesc; + + //! GDAL projection string for the initial intervention class raster file + string m_strGDALICProjection; + + //! GDAL data type of the initial intervention class raster file + string m_strGDALICDataType; + + //! GDAL code for the initial intervention height raster file + string m_strGDALIHDriverCode; + + //! GDAL description for the initial intervention height raster file + string m_strGDALIHDriverDesc; + + //! GDAL projection string for the initial intervention height raster file + string m_strGDALIHProjection; + + //! GDAL data type for the initial intervention height raster file + string m_strGDALIHDataType; + + //! GDAL code for the initial water depth raster file + string m_strGDALIWDriverCode; + + //! GDAL description for the initial water depth raster file + string m_strGDALIWDriverDesc; + + //! GDAL projection string for the initial water depth raster file + string m_strGDALIWProjection; + + //! GDAL data type for the initial water depth raster file + string m_strGDALIWDataType; + + //! GDAL code for the initial suspended sediment raster file + string m_strGDALISSDriverCode; + + //! GDAL description for the initial suspended sediment raster file + string m_strGDALISSDriverDesc; + + //! GDAL projection string for the initial suspended sediment raster file + string m_strGDALISSProjection; + + //! GDAL data type for the initial suspended sediment raster file + string m_strGDALISSDataType; + + //! GDAL code for the deep water wave stations vector file + string m_strOGRDWWVDriverCode; + + // GDAL description for the deep water wave stations vector file + string m_strOGRDWWVDriverDesc; + + //! GDAL geometry for the deep water wave stations vector file + string m_strOGRDWWVGeometry; + + //! GDAL data type for the deep water wave stations vector file + string m_strOGRDWWVDataType; + + //! GDAL code for the sediment input event locations vector file + string m_strOGRSedInputDriverCode; + + // GDAL description for the sediment input event locations vector file + string m_strOGRSedInputDriverDesc; + + //! GDAL geometry for the sediment input event locations vector file + string m_strOGRSedInputGeometry; + + //! GDAL data type for the sediment input event locations vector file + string m_strOGRSedInputDataType; + + //! GDAL code for the flood input locations point or vector file + string m_strOGRFloodDriverCode; + + // GDAL description for the flood input locations point or vector file + string m_strOGRFloodDriverDesc; + + //! GDAL geometry for the flood input locations point or vector file + string m_strOGRFloodGeometry; + + //! GDAL data type for the flood input locations point or vector file + string m_strOGRFloodDataType; + + //! GDAL raster output driver long name + string m_strGDALRasterOutputDriverLongname; + + //! GDAL raster output driver file extension + string m_strGDALRasterOutputDriverExtension; + + //! GDAL-OGR vector output drive file extension + string m_strOGRVectorOutputExtension; + + //! The name of this simulation + string m_strRunName; + + //! The duration units for this simulation + string m_strDurationUnits; + + //! The name of the deep water wave stations shape file + string m_strDeepWaterWaveStationsShapefile; + + //! The name of the deep water wave stations time series file + string m_strDeepWaterWavesInputFile; + + //! The name of the sediment input events shape file + string m_strSedimentInputEventShapefile; + + //! The name of the sediment input events time series file + string m_strSedimentInputEventFile; + + //! The name of the flood loction events shape file + string m_strFloodLocationShapefile; + + //! System start-simulation time + time_t m_tSysStartTime; + + //! System finish-simulation time + time_t m_tSysEndTime; + + //! The main output file stream + ofstream OutStream; + + //! Sea area time series file output stream + ofstream SeaAreaTSStream; + + //! SWL time series file output stream + ofstream SWLTSStream; + + //! Shore platform erosion time series file output stream + ofstream PlatformErosionTSStream; + + //! Cliff collapse erosion time series file output stream + ofstream CliffCollapseErosionTSStream; + + //! Cliff collapse deposition time series file output stream + ofstream CliffCollapseDepositionTSStream; + + //! Cliff collapse net change (erosion - deposition) time series file output stream + ofstream CliffCollapseNetChangeTSStream; + + //! Beach sediment erosion time series file output stream + ofstream BeachErosionTSStream; + + //! Beach sediment deposition time series file output stream + ofstream BeachDepositionTSStream; + + //! Beach sediment net change (erosion - deposition) time series file output stream + ofstream BeachSedimentNetChangeTSStream; + + //! Fine sediment in suspension time series file output stream + ofstream FineSedSuspensionTSStream; + + //! Flood setup surge time series file output stream + ofstream FloodSetupSurgeTSStream; + + //! Flood setup surge runup time series file output stream + ofstream FloodSetupSurgeRunupTSStream; + + //! Cliff notch elevation time series file output stream + ofstream CliffNotchElevTSStream; + + //! One element per layer: has the consolidated sediment of this layer been changed during this iteration? + vector m_bConsChangedThisIter; + + //! One element per layer: has the consolidated sediment of this layer been changed during this iteration? + vector m_bUnconsChangedThisIter; + + //! The numbers of the profiles which are to be saved + vector m_VnProfileToSave; + + //! ID for deep water wave station, this corresponds with the ID in the wave time series file + vector m_VnDeepWaterWaveStationID; + + //! ID for sediment input location, this corresponds with the ID in the sediment input time series file + vector m_VnSedimentInputLocationID; + + //! ID for flood location + vector m_VnFloodLocationID; + + //! Savitzky-Golay shift index for the coastline vector(s) + vector m_VnSavGolIndexCoast; + + //! Timesteps at which to save profiles + vector m_VulProfileTimestep; + + //! Calculate deep water wave values at these timesteps + vector m_VlDeepWaterWaveValuesAtTimestep; + + //! Elevations for raster slice output + vector m_VdSliceElev; + + //! For erosion potential lookup + vector m_VdErosionPotential; + + //! For erosion potential lookup + vector m_VdDepthOverDB; + + //! Savitzky-Golay filter coefficients for the coastline vector(s) + vector m_VdSavGolFCRWCoast; + + //! Savitzky-Golay filter coefficients for the profile vectors + vector m_VdSavGolFCGeomProfile; + + //! Tide data: one record per timestep, is the change (m) from still water level for that timestep + vector m_VdTideData; + + //! X coordinate (grid CRS) for deep water wave station + vector m_VdDeepWaterWaveStationX; + + //! Y coordinate (grid CRS) for deep water wave station + vector m_VdDeepWaterWaveStationY; + + //! This-iteration wave height at deep water wave station + vector m_VdThisIterDeepWaterWaveStationHeight; + + //! This-iteration wave orientation at deep water wave station + vector m_VdThisIterDeepWaterWaveStationAngle; + + //! This-iteration wave period at deep water wave station + vector m_VdThisIterDeepWaterWaveStationPeriod; + + //! Time series of wave heights at deep water wave station + vector m_VdTSDeepWaterWaveStationHeight; + + //! Time series of wave orientation at deep water wave station + vector m_VdTSDeepWaterWaveStationAngle; + + //! Time series of wave period at deep water wave station + vector m_VdTSDeepWaterWaveStationPeriod; + + //! Storage for wave transect points (real and synthetic) for debug output + vector m_VAllTransectsWithSynthetic; + + //! X coordinate (grid CRS) for sediment input event + vector m_VdSedimentInputLocationX; + + //! X coordinate (grid CRS) for sediment input event + vector m_VdSedimentInputLocationY; + + //! X coordinate (grid CRS) for total water level flooding + vector m_VdFloodLocationX; + + //! X coordinate (grid CRS) for total water level flooding + vector m_VdFloodLocationY; + + //! The name of the initial fine-sized unconsolidated sediment GIS file + vector m_VstrInitialFineUnconsSedimentFile; + + //! The name of the initial sand-sized unconsolidated sediment GIS file + vector m_VstrInitialSandUnconsSedimentFile; + + //! The name of the initial coarse-sized unconsolidated sediment GIS file + vector m_VstrInitialCoarseUnconsSedimentFile; + + //! The name of the initial fine-sized consolidated sediment GIS file + vector m_VstrInitialFineConsSedimentFile; + + //! The name of the initial sand-sized consolidated sediment GIS file + vector m_VstrInitialSandConsSedimentFile; + + //! The name of the initial coarse-sized consolidated sediment GIS file + vector m_VstrInitialCoarseConsSedimentFile; + + //! GDAL driver code for the initial unconsolidated fine sediment GIS data + vector m_VstrGDALIUFDriverCode; + + //! GDAL driver description for the initial unconsolidated fine sediment GIS data + vector m_VstrGDALIUFDriverDesc; + + //! GDAL projection for the initial unconsolidated fine sediment GIS data + vector m_VstrGDALIUFProjection; + + //! GDAL data type for the initial unconsolidated fine sediment GIS data + vector m_VstrGDALIUFDataType; + + //! GDAL driver code for the initial unconsolidated sand sediment GIS data + vector m_VstrGDALIUSDriverCode; + + //! GDAL driver description for the initial unconsolidated sand sediment GIS data + vector m_VstrGDALIUSDriverDesc; + + //! GDAL projection for the initial unconsolidated sand sediment GIS data + vector m_VstrGDALIUSProjection; + + //! GDAL data type for the initial unconsolidated sand sediment GIS data + vector m_VstrGDALIUSDataType; + + //! GDAL driver code for the initial unconsolidated coarse sediment GIS data + vector m_VstrGDALIUCDriverCode; + + //! GDAL driver description for the initial unconsolidated coarse sediment GIS data + vector m_VstrGDALIUCDriverDesc; + + //! GDAL projection for the initial unconsolidated coarse sediment GIS data + vector m_VstrGDALIUCProjection; + + //! GDAL data type for the initial unconsolidated coarse sediment GIS data + vector m_VstrGDALIUCDataType; + + //! GDAL driver code for the initial consolidated fine sediment GIS data + vector m_VstrGDALICFDriverCode; + + //! GDAL driver description for the initial consolidated fine sediment GIS data + vector m_VstrGDALICFDriverDesc; + + //! GDAL projection for the initial consolidated fine sediment GIS data + vector m_VstrGDALICFProjection; + + //! GDAL data type for the initial consolidated fine sediment GIS data + vector m_VstrGDALICFDataType; + + //! GDAL driver code for the initial consolidated sand sediment GIS data + vector m_VstrGDALICSDriverCode; + + //! GDAL driver description for the initial consolidated sand sediment GIS data + vector m_VstrGDALICSDriverDesc; + + //! GDAL dprojection for the initial consolidated sand sediment GIS data + vector m_VstrGDALICSProjection; + + //! GDAL data type for the initial consolidated sand sediment GIS data + vector m_VstrGDALICSDataType; + + //! GDAL driver code for the initial consolidated coarse sediment GIS data + vector m_VstrGDALICCDriverCode; + + //! GDAL driver decription for the initial consolidated coarse sediment GIS data + vector m_VstrGDALICCDriverDesc; + + //! GDAL projection for the initial consolidated coarse sediment GIS data + vector m_VstrGDALICCProjection; + + //! GDAL data type for the initial consolidated coarse sediment GIS data + vector m_VstrGDALICCDataType; + + //! Pointer to the raster grid object + CGeomRasterGrid* m_pRasterGrid; + + //! The coastline objects + vector m_VCoast; + + //! Coastline (external CRS) at the highest SWL so far during this simulation + vector m_VHighestSWLCoastLine; + + //! Coastline (external CRS) at the lowest SWL so far during this simulation + vector m_VLowestSWLCoastLine; + + //! The traced cliff toe lines (in external CRS) + vector m_VCliffToe; + + //! TODO 007 Finish surge and runup stuff + vector m_VFloodWaveSetupSurge; + + //! TODO 007 Finish surge and runup stuff + vector m_VFloodWaveSetupSurgeRunup; + + //! Edge cells + vector m_VEdgeCell; + + //! The grid edge that each edge cell belongs to + vector m_VEdgeCellEdge; + + //! The location to compute the total water level for flooding + vector m_VCellFloodLocation; + + //! Sediment input events + vector m_pVSedInputEvent; + + //! The c++11 random number generators + default_random_engine m_Rand[NUMBER_OF_RNGS]; + + //! c++11 unit normal distribution (mean = 0, stdev = 1) + normal_distribution m_dGetFromUnitNormalDist{0.0, 1.0}; + + private: + // Input and output routines + int nHandleCommandLineParams(int, char const*[]); + bool bReadIniFile(void); + bool bReadRunDataFile(void); + bool bReadYamlFile(void); + bool bDetectFileFormat(string const& strFileName, bool& bIsYaml); + bool bConfigureFromDatFile(CConfiguration& config); + bool bConfigureFromYamlFile(CConfiguration& config); + bool bApplyConfiguration(CConfiguration const& config); + bool bOpenLogFile(void); + bool bSetUpTSFiles(void); + void WriteStartRunDetails(void); + bool bWritePerTimestepResults(void); + bool bWritePerTimestepResultsFixedWidth(void); + bool bWritePerTimestepResultsCSV(void); + bool bWriteTSFiles(void); + int nWriteEndRunDetails(void); + int nReadShapeFunctionFile(void); + int nReadWaveStationInputFile(int const); + int nReadSedimentInputEventFile(void); + int nReadTideDataFile(void); + int nSaveProfile(int const, CGeomProfile const*, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; + bool bWriteProfileData(int const, CGeomProfile const*, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; + int nSaveParProfile(int const, CGeomProfile const*, int const, int const, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; + bool bWriteParProfileData(int const, int const, int const, int const, int const, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector const*, vector*const, vector const*) const; + void WriteLookUpData(void); + + // GIS input and output stuff + void InitializeGDALPerformance(void); + int nReadRasterBasementDEM(void); + int nReadRasterGISFile(int const, int const); + int nReadVectorGISFile(int const); + bool bWriteRasterGISFile(int const, string const*, int const = 0, double const = 0); + bool bWriteVectorGISFile(int const, string const*); + void GetRasterOutputMinMax(int const, double&, double&, int const, double const); + void SetRasterFileCreationDefaults(void); + int nInterpolateWavesToPolygonCells(vector const*, vector const*, vector const*, vector const*, vector const*); + + // Initialization + bool bCreateErosionPotentialLookUp(vector*, vector*, vector*); + void CalcMHWElevation(int const); + + // Top-level simulation routines + static int nUpdateIntervention(void); + int nCheckForSedimentInputEvent(void); + int nCalcExternalForcing(void); + int nInitGridAndCalcStillWaterLevel(void); + int nLocateSeaAndCoasts(void); + int nLocateFloodAndCoasts(void); + int nAssignLandformsForAllCoasts(void); + int nAssignLandformsForAllCells(void); + int nDoAllPropagateWaves(void); + void GenerateSyntheticTransects(vector const*, vector*); + int nDoAllShorePlatFormErosion(void); + int nDoAllWaveEnergyToCoastLandforms(void); + int nDoCliffCollapse(int const, CRWCliff *, double&, double&, double&, int&, double&, double&); + void DoCliffCollapseTalusDeposition(int const, CRWCliff const*, double const, double const, int const); + int nMoveCliffTalusToUnconsolidated(void); + int nUpdateGrid(void); + + // For cliff toe location + int nLocateCliffToe(void); + void nCalcSlopeAtAllCells(void); + void nLocateCliffCell(void); + void nTraceSeawardCliffEdge(void); + void nValidateCliffToeEdges(void); + CGeomLine nValidateCliffToeDirection(CGeomLine& CliffEdge, bool bReverse); + void nRemoveSmallCliffIslands(int const); + + // Lower-level simulation routines + void FindAllSeaCells(void); + int FindAllInundatedCells(void); + void CellByCellFillSea(int const, int const); + void FloodFillLand(int const, int const); + int nTraceCoastLine(unsigned int const, int const, int const, vector*, vector const*); + int nTraceAllCoasts(void); + int nTraceFloodCoastLine(unsigned int const, int const, int const, vector*, vector const*); + int nTraceAllFloodCoasts(void); + void DoCoastCurvature(int const, int const); + int nCheckAndMarkAllProfiles(void); + int nCreateAllProfiles(void); + void LocateAndCreateProfiles(int const, int&, vector*, vector> const*); + int nCreateProfile(int const, int const, int const, int const, bool const, CGeom2DIPoint const*); + int nLocateAndCreateGridEdgeProfile(bool const, int const, int&); + void MarkProfilesOnGrid(int const, int&); + void CheckForIntersectingProfiles(void); + static bool bCheckForIntersection(CGeomProfile* const, CGeomProfile* const, int&, int&, double&, double&, double&, double&); + void MergeProfilesAtFinalLineSegments(int const, CGeomProfile*, CGeomProfile*, int const, int const, double const, double const, double const, double const); + void TruncateOneProfileRetainOtherProfile(int const, CGeomProfile*, CGeomProfile*, double, double, int, int, bool const); + int nInsertPointIntoProfilesIfNeededThenUpdate(int const, CGeomProfile*, double const, double const, int const, CGeomProfile*, int const, bool const); + void TruncateProfileAndAppendNew(int const, CGeomProfile*, int const, vector const*, vector>> const*); + void CreateRasterizedProfile(int const, CGeomProfile*, vector*, vector*, bool&, bool&, bool&, bool&, bool&, bool&); + static void CalcDeanProfile(vector*, double const, double const, double const, bool const, int const, double const); + static double dSubtractProfiles(vector const*, vector const*, vector const*); + int nCalcPotentialPlatformErosionOnProfile(int const, CGeomProfile*); + int nCalcPotentialPlatformErosionBetweenProfiles(int const, CGeomProfile*, int const); + void ConstructParallelProfile(int const, int const, int const, int const, int const, vector* const, vector*, vector *); + double dCalcBeachProtectionFactor(int const, int const, double const); + void FillInBeachProtectionHolesAndRemoveLegacyCliffs(void); + void FillPotentialPlatformErosionHoles(void); + void DoActualPlatformErosionOnCell(int const, int const); + double dLookUpErosionPotential(double const); + static CGeom2DPoint PtChooseEndPoint(int const, CGeom2DPoint const*, CGeom2DPoint const*, double const, double const, double const, double const); + int nGetCoastNormalEndPoint(int const, int const, int const, CGeom2DPoint const*, double const, CGeom2DPoint *, CGeom2DIPoint *, bool const); + int nLandformToGrid(int const, int const); + int nCalcWavePropertiesOnProfile(int const, int const, CGeomProfile*, vector*, vector*, vector*, vector*, vector*); + int nGetThisProfileElevationsForCShore(int const, CGeomProfile*, int const, vector*, vector*, vector*); + int nCreateCShoreInfile(int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, int const, double const, double const, double const, double const, double const, double const, double const, double const, vector const*, vector const*, vector const*); + int nReadCShoreOutput(int const, string const*, int const, int const, vector const*, vector*); + /* static */ void InterpolateCShoreOutput(vector const*, int const, int const, vector*, vector*, vector*, vector*, vector*, vector*, vector*, vector*, vector*); + static double dCalcWaveAngleToCoastNormal(double const, double const, int const); + void CalcCoastTangents(int const); + void InterpolateWavePropertiesBetweenProfiles(int const, int const); + void InterpolateWaveHeightToCoastPoints(int const); + // void InterpolateWavePropertiesToCells(int const, int const, int const); + void ModifyBreakingWavePropertiesWithinShadowZoneToCoastline(int const, int const); + static double dCalcCurvature(int const, CGeom2DPoint const*, CGeom2DPoint const*, CGeom2DPoint const*); + void CalcD50AndFillWaveCalcHoles(void); + int nDoAllShadowZones(void); + static bool bOnOrOffShoreAndUpOrDownCoast(double const, double const, int const, bool&); + static CGeom2DIPoint PtiFollowWaveAngle(CGeom2DIPoint const*, double const, double&); + // int nFindAllShadowZones(void); + int nCellByCellFillShadowZone(int const, int const, CGeom2DIPoint const*, CGeom2DIPoint const*, CGeom2DIPoint const*); + void DoShadowZoneAndDownDriftZone(int const, int const, int const, int const); + void ProcessDownDriftCell(int const, int const, int const, double const, int const); + void ProcessShadowZoneCell(int const, int const, int const, CGeom2DIPoint const*, int const, int const, int const); + int nCreateAllPolygons(void); + void RasterizePolygonJoiningLine(int const, CGeom2DIPoint const*, CGeom2DIPoint const*, int const); + // static bool bIsWithinPolygon(CGeom2DPoint const*, vector const*); + // static CGeom2DPoint PtFindPointInPolygon(vector const*, int const); + void MarkPolygonCells(void); + int nDoPolygonSharedBoundaries(void); + void DoAllPotentialBeachErosion(void); + int nDoAllActualBeachErosionAndDeposition(void); + int nDoParallelProfileUnconsErosion(CGeomCoastPolygon *, int const, int const, int const, int const, int const, int const, vector const*, vector const*, double&, double&, double&); + void ErodeCellBeachSedimentSupplyLimited(int const, int const, int const, int const, double const, double&); + int nDoUnconsErosionOnPolygon(int const, CGeomCoastPolygon *, int const, double const, double&); + int nDoUnconsDepositionOnPolygon(int const, CGeomCoastPolygon *, int const, double, double&); + void CalcDepthOfClosure(void); + int nInterpolateAllDeepWaterWaveValues(void); + int nSetAllCoastpointDeepWaterWaveValues(void); + int nDoSedimentInputEvent(int const); + void AllPolygonsUpdateStoredUncons(int const); + bool bIsInterventionCell(int const, int const) const; + bool bSurroundedByDriftCells(int const, int const); + bool bElevAboveDeanElev(int const, int const, double const, CRWCellLandform const*); + // void CreatePolygonIndexIDSeq(int const); + int nDoMultipleCoastlines(void); + int nTruncateProfilesDifferentCoasts(int const, int const, int const, int const, int const, int const); + int nTruncateProfileHitDifferentCoast(int const, int const, int const, int const); + int nTruncateProfileMultiLineDifferentCoasts(CGeomProfile*, double const, double const); + bool bIncreaseCliffNotchIncision(int const, int const, int const, CRWCliff*, double const); + bool bCreateNotchInland(int const, int const, int const, int const, double const, double const); + + // GIS utility routines + int nMarkBoundingBoxEdgeCells(void); + bool bCheckRasterGISOutputFormat(void); + bool bCheckVectorGISOutputFormat(void); + bool bSaveAllRasterGISFiles(void); + bool bSaveAllVectorGISFiles(void); + bool bIsWithinValidGrid(int const, int const) const; + bool bIsWithinValidGrid(CGeom2DIPoint const*) const; + double dGridCentroidXToExtCRSX(int const) const; + double dGridCentroidYToExtCRSY(int const) const; + double dGridXToExtCRSX(double const) const; + double dGridYToExtCRSY(double const) const; + // double dExtCRSXToGridCentroidX(double const) const; + // double dExtCRSYToGridCentroidY(double const) const; + CGeom2DIPoint PtiExtCRSToGridRound(CGeom2DPoint const*) const; + CGeom2DPoint PtGridCentroidToExt(CGeom2DIPoint const*) const; + double dExtCRSXToGridX(double const) const; + double dExtCRSYToGridY(double const) const; + static double dGetDistanceBetween(CGeom2DPoint const*, CGeom2DPoint const*); + static double dGetDistanceBetween(CGeom2DIPoint const*, CGeom2DIPoint const*); + static double dGetDistanceBetween(double const, double const, double const, double const); + static double dTriangleAreax2(CGeom2DPoint const*, CGeom2DPoint const*, CGeom2DPoint const*); + void KeepWithinValidGrid(int&, int&) const; + void KeepWithinValidGrid(int, int, int&, int&) const; + void KeepWithinValidGrid(CGeom2DIPoint const*, CGeom2DIPoint *) const; + static double dKeepWithin360(double const); + // vector VGetPerpendicular(CGeom2DPoint const*, CGeom2DPoint + // const*, double const, int const); + // static CGeom2DPoint PtGetPerpendicular(CGeom2DPoint const*, CGeom2DPoint const*, double const, int const); + static CGeom2DIPoint PtiGetPerpendicular(CGeom2DIPoint const*, CGeom2DIPoint const*, double const, int const); + static CGeom2DIPoint PtiGetPerpendicular(int const, int const, int const, int const, double const, int const); + static CGeom2DPoint PtAverage(CGeom2DPoint const*, CGeom2DPoint const*); + static CGeom2DPoint PtAverage(vector*); + // static CGeom2DIPoint PtiAverage(CGeom2DIPoint const*, CGeom2DIPoint const*); + // static CGeom2DIPoint PtiAverage(vector*); + static CGeom2DIPoint PtiWeightedAverage(CGeom2DIPoint const*, CGeom2DIPoint const*, double const); + static CGeom2DIPoint PtiPolygonCentroid(vector*); + static double dAngleSubtended(CGeom2DIPoint const*, CGeom2DIPoint const*, CGeom2DIPoint const*); + static int nGetOppositeDirection(int const); + // static void GetSlopeAndInterceptFromPoints(CGeom2DIPoint const*, + // CGeom2DIPoint const*, double&, double&); + CGeom2DIPoint PtiFindClosestCoastPoint(int const, int const, int&); + int nFindClosestCoastPoint(int const, int const, int&); + int nConvertMetresToNumCells(double const) const; + bool bIsAdjacentEdgeCell(CGeom2DIPoint const*, CGeom2DIPoint const*); + void FindClosestPointOnStraightLine(double const, double const, double const, double const, double const, double const, double&, double&); + + // Interpolation routines + double dGetInterpolatedValue(vector const*, vector const*, double, bool); + double dGetInterpolatedValue(vector const*, vector const*, int, bool); + int nFindIndex(vector const*, double const); + vector VdInterpolateCShoreProfileOutput(vector const*, vector const*, vector const*); + + // Utility routines + static void AnnounceStart(void); + void AnnounceLicence(void); + void AnnounceReadBasementDEM(void) const; + static void AnnounceAddLayers(void); + static void AnnounceReadRasterFiles(void); + static void AnnounceReadVectorFiles(void); + void AnnounceReadLGIS(void) const; + void AnnounceReadICGIS(void) const; + void AnnounceReadIHGIS(void) const; + static void AnnounceFinalInitialization(void); + void AnnounceReadInitialSuspSedGIS(void) const; + void AnnounceReadInitialFineUnconsSedGIS(int const) const; + void AnnounceReadInitialSandUnconsSedGIS(int const) const; + void AnnounceReadInitialCoarseUnconsSedGIS(int const) const; + void AnnounceReadInitialFineConsSedGIS(int const) const; + void AnnounceReadInitialSandConsSedGIS(int const) const; + void AnnounceReadInitialCoarseConsSedGIS(int const) const; + void AnnounceReadDeepWaterWaveValuesGIS(void) const; + void AnnounceReadSedimentEventInputValuesGIS(void) const; + void AnnounceReadFloodLocationGIS(void) const; + void AnnounceReadTideData(void) const; + static void AnnounceReadSCAPEShapeFunctionFile(void); + static void AnnounceAllocateMemory(void); + static void AnnounceIsRunning(void); + static void AnnounceSimEnd(void); + void StartClock(void); + bool bFindExeDir(char const*); + bool bTimeToQuit(void); + static int nDoTimeUnits(string const*); + int nDoSimulationTimeMultiplier(string const*); + static double dGetTimeMultiplier(string const*); + static bool bParseDate(string const*, int&, int&, int&); + static bool bParseTime(string const*, int&, int&, int&); + void DoEndOfTimestepTotals(void); + static string strGetBuild(void); + static string strGetComputerName(void); + void DoCPUClockReset(void); + // void CalcTime(double const); + static string strDispTime(double const, bool const, bool const); + static string strDispSimTime(double const); + void AnnounceProgress(void); + static string strGetErrorText(int const); + string strListRasterFiles(void) const; + string strListVectorFiles(void) const; + string strListTSFiles(void) const; + void CalcProcessStats(void); + void CalcSavitzkyGolayCoeffs(void); + CGeomLine LSmoothCoastSavitzkyGolay(CGeomLine *, int const, int const) const; + CGeomLine LSmoothCoastRunningMean(CGeomLine *) const; + vector dVSmoothProfileSlope(vector*) const; + // vector dVCalCGeomProfileSlope(vector*, vector*); // TODO 007 Why was this removed? vector + // dVSmoothProfileSavitzkyGolay(vector*, vector*); // + // TODO 007 was this removed? vector + // dVSmoothProfileRunningMean(vector*); // + // TODO 007 was this removed? + static void CalcSavitzkyGolay(double[], int const, int const, int const, int const, int const); + static string pstrChangeToBackslash(string const*); + static string pstrChangeToForwardSlash(string const*); + static string strTrim(string const*); + static string strTrimLeft(string const*); + static string strTrimRight(string const*); + static string strToLower(string const*); + // static string strToUpper(string const*); + static string strRemoveSubstr(string*, string const*); + static vector* VstrSplit(string const*, char const, vector*); + static vector VstrSplit(string const*, char const); + // static double dCrossProduct(double const, double const, double const, + // double const, double const, double const); static double + // dGetMean(vector const*); static double dGetStdDev(vector + // const*); + static void AppendEnsureNoGap(vector*, CGeom2DIPoint const*); + // static bool bIsNumeric(string const*); + unsigned long ulConvertToTimestep(string const*) const; + void WritePolygonInfoTable(void); + void WritePolygonPreExistingSedimentTable(void); + void WritePolygonSedimentInputEventTable(void); + void WritePolygonShorePlatformErosion(void); + void WritePolygonCliffCollapseErosion(void); + void WritePolygonSedimentBeforeMovement(void); + void WritePolygonPotentialErosion(void); + // void WritePolygonUnconsErosion(int const); + void WritePolygonUnsortedSequence(vector>>&); + void WritePolygonSortedSequence(vector>>&); + void WritePolygonActualMovement(vector>>&); + void DoEndOfRunDeletes(void); + + protected: + public: + ofstream LogStream; + + CSimulation(void); + ~CSimulation(void); + + //! Returns the NODATA value + double dGetMissingValue(void) const; + + //! Returns this timestep's SWL + double dGetThisIterSWL(void) const; + + //! Returns this timestep's total water level TODO 007 Finish surge and runup stuff + double dGetThisIterTotWaterLevel(void) const; + + // //! Returns the vertical tolerance for beach cells to be included in smoothing + // double dGetMaxBeachElevAboveSWL(void) const; + + //! Returns the cell size + // double dGetCellSide(void) const; + + //! Returns the size of the grid in the X direction + int nGetGridXMax(void) const; + + //! Returns the size of the grid in the Y direction + int nGetGridYMax(void) const; + + //! Returns the global d50 value for fine sediment + double dGetD50Fine(void) const; + + //! Returns the global d50 value for sand sediment + double dGetD50Sand(void) const; + + //! Returns the global d50 value for coarse sediment + double dGetD50Coarse(void) const; + + //! Runs the simulation + int nDoSimulation(int, char const*[]); + + //! Carries out end-of-simulation tidying (error messages etc.) + void DoSimulationEnd(int const); +}; +#endif // SIMULATION_H diff --git a/src/utils.cpp b/src/utils.cpp index b7e5ebf99..9fb424dcf 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,3134 +1,3134 @@ -/*! - \file utils.cpp - \brief Utility routines - \details TODO 001 A more detailed description of this routine. - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License -*/ - -/* ============================================================================================================================== - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -==============================================================================================================================*/ -#include - -#ifdef _WIN32 -#include // Needed for CalcProcessStats() -#include -#include // For isatty() -#elif defined __GNUG__ -#include // Needed for CalcProcessStats() -#include // For isatty() -#include -#endif - -#ifdef _OPENMP -#include -#endif - -#include - -#include - -#include - -#include -using std::tolower; - -#include -using std::floor; - -#include -using std::clock; -using std::clock_t; -using std::difftime; -using std::localtime; -using std::time; - -#include -using std::fixed; - -#include -using std::cerr; -using std::cout; -using std::endl; -using std::ios; - -#include -using std::put_time; -using std::setprecision; -using std::setw; - -#include -using std::to_string; - -#include -using std::stringstream; - -#include -using std::transform; - -#include - -#include "cme.h" -#include "simulation.h" -#include "coast.h" -#include "2di_point.h" - -//=============================================================================================================================== -//! Returns the missing value code for double precision -//=============================================================================================================================== -double CSimulation::dGetMissingValue(void) const -{ - return m_dMissingValue; -} - -//=============================================================================================================================== -//! Returns the still water level (SWL) -//=============================================================================================================================== -double CSimulation::dGetThisIterSWL(void) const -{ - return m_dThisIterSWL; -} - -//=============================================================================================================================== -//! Returns the this-iteration total water level TODO 007 Finish surge and runup stuff -//=============================================================================================================================== -double CSimulation::dGetThisIterTotWaterLevel(void) const -{ - return m_dThisIterDiffTotWaterLevel; -} - -// //=============================================================================================================================== -// //! Returns the max elevation of the beach above SWL -// //=============================================================================================================================== -// double CSimulation::dGetMaxBeachElevAboveSWL (void) const -// { -// return m_dMaxBeachElevAboveSWL; -// } - -//=============================================================================================================================== -// Returns the cell side length -//=============================================================================================================================== -// double CSimulation::dGetCellSide(void) const -// { -// return m_dCellSide; -// } - -//=============================================================================================================================== -//! Returns X grid max -//=============================================================================================================================== -int CSimulation::nGetGridXMax(void) const -{ - return m_nXGridSize; -} - -//=============================================================================================================================== -//! Returns Y grid max -//=============================================================================================================================== -int CSimulation::nGetGridYMax(void) const -{ - return m_nYGridSize; -} - -//=============================================================================================================================== -//! Returns D50 for fine sediment -//=============================================================================================================================== -double CSimulation::dGetD50Fine(void) const -{ - return m_dD50Fine; -} - -//=============================================================================================================================== -//! Returns D50 for sand sediment -//=============================================================================================================================== -double CSimulation::dGetD50Sand(void) const -{ - return m_dD50Sand; -} - -//=============================================================================================================================== -//! Returns D50 for coarse sediment -//=============================================================================================================================== -double CSimulation::dGetD50Coarse(void) const -{ - return m_dD50Coarse; -} - -//=============================================================================================================================== -//! Handles command-line parameters -//=============================================================================================================================== -int CSimulation::nHandleCommandLineParams(int nArg, char const* pcArgv[]) -{ - if ((!isatty(fileno(stdout))) || (!isatty(fileno(stderr)))) - // Running with stdout or stderr not a tty, so either redirected or running as a background job. Ignore all command line parameters - return RTN_OK; - - // Process the parameters following the name of the executable - for (int i = 1; i < nArg; i++) - { - string strArg = pcArgv[i]; - strArg = strTrim(&strArg); - -#ifdef _WIN32 - // Swap any forward slashes to backslashes - strArg = pstrChangeToBackslash(&strArg); -#endif - - if (strArg.find("--gdal") != string::npos) - { - // User wants to know what GDAL raster drivers are available - cout << GDAL_DRIVERS << endl - << endl; - - for (int j = 0; j < GDALGetDriverCount(); j++) - { - GDALDriverH hDriver = GDALGetDriver(j); - - string strTmp(GDALGetDriverShortName(hDriver)); - strTmp.append(" "); - strTmp.append(GDALGetDriverLongName(hDriver)); - - cout << strTmp << endl; - } - - return (RTN_HELP_ONLY); - } - - else - { - if (strArg.find("--about") != string::npos) - { - // User wants information about CoastalME - cout << ABOUT << endl; - cout << THANKS << endl; - - return (RTN_HELP_ONLY); - } - - else - { - if (strArg.find("--yaml") != string::npos) - { - // User wants to use YAML format for input datafile - m_bYamlInputFormat = true; - } - - else if (strArg.find("--home") != string::npos) - { - // Read in user defined runtime directory - // string strTmp; - - // Find the position of '=' - size_t const pos = strArg.find('='); - - // Was '=' found? - if (pos != string::npos) - { - // Yes, so get the substring after '=' and assign it to the global variable - m_strCMEIni = strArg.substr(pos + 1); - } - - else - { - // No - cout << "No '=' found in the input string" << endl; - } - - return (RTN_OK); - } - - // TODO 049 Handle other command line parameters e.g. path to .ini file, path to datafile - else - { - // Display usage information - cout << USAGE << endl; - cout << USAGE1 << endl; - cout << USAGE2 << endl; - cout << USAGE3 << endl; - cout << USAGE4 << endl; - cout << USAGE5 << endl; - cout << USAGE6 << endl; - - return (RTN_HELP_ONLY); - } - } - } - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! Tells the user that we have started the simulation -//=============================================================================================================================== -void CSimulation::AnnounceStart(void) -{ - cout << endl - << PROGRAM_NAME << " for " << PLATFORM << " " << strGetBuild() << endl; - #ifdef _OPENMP - cout << "OpenMP is ENABLED" << endl; - cout << "Max threads available: " << omp_get_max_threads() << endl; - #pragma omp parallel - { - #pragma omp single - std::cout << "Actually running with " << omp_get_num_threads() << " threads" << std::endl; - } - #else - std::cout << "OpenMP is NOT ENABLED - code will run serially!" << std::endl; - #endif // !_OPENMP -} - -//=============================================================================================================================== -//! Starts the clock ticking -//=============================================================================================================================== -void CSimulation::StartClock(void) -{ - // First start the 'CPU time' clock ticking - if (static_cast(-1) == clock()) - { - // There's a problem with the clock, but continue anyway - LogStream << NOTE << "CPU time not available" << endl; - m_dCPUClock = -1; - } - - else - { - // All OK, so get the time in m_dClkLast (this is needed to check for clock rollover on long runs) - m_dClkLast = static_cast(clock()); - m_dClkLast -= CLOCK_T_MIN; // necessary if clock_t is signed to make m_dClkLast unsigned - } - - // And now get the actual time we started - m_tSysStartTime = time(nullptr); -} - -//=============================================================================================================================== -//! Finds the folder (directory) in which the CoastalME executable is located -//=============================================================================================================================== -bool CSimulation::bFindExeDir(char const* pcArg) -{ - string strTmp; - char szBuf[BUF_SIZE] = ""; - -#ifdef _WIN32 - - if (0 != GetModuleFileName(NULL, szBuf, BUF_SIZE)) - strTmp = szBuf; - - else - // It failed, so try another approach - strTmp = pcArg; - -#else - // char* pResult = getcwd(szBuf, BUF_SIZE); // Used to use this, but what if cwd is not the same as the CoastalME dir? - - if (-1 != readlink("/proc/self/exe", szBuf, BUF_SIZE)) - strTmp = szBuf; - - else - // It failed, so try another approach - strTmp = pcArg; - -#endif - - // Neither approach has worked, so give up - if (strTmp.empty()) - return false; - - // It's OK, so trim off the executable's name - int const nPos = static_cast(strTmp.find_last_of(PATH_SEPARATOR)); - m_strCMEDir = strTmp.substr(0, nPos + 1); // Note that this must be terminated with a backslash - - return true; -} -//=============================================================================================================================== -//! Tells the user about the licence -//=============================================================================================================================== -void CSimulation::AnnounceLicence(void) -{ - cout << COPYRIGHT << endl - << endl; - cout << LINE << endl; - cout << DISCLAIMER1 << endl; - cout << DISCLAIMER2 << endl; - cout << DISCLAIMER3 << endl; - cout << DISCLAIMER4 << endl; - cout << DISCLAIMER5 << endl; - cout << DISCLAIMER6 << endl; - cout << LINE << endl - << endl; - - cout << START_NOTICE << strGetComputerName() << " at " << put_time(localtime(&m_tSysStartTime), "%T on %A %d %B %Y") << endl; - cout << INITIALIZING_NOTICE << endl; -} - -//=============================================================================================================================== -//! Given a string containing time units, this returns the appropriate multiplier -//=============================================================================================================================== -double CSimulation::dGetTimeMultiplier(string const* strIn) -{ - // First decide what the time units are - int const nTimeUnits = nDoTimeUnits(strIn); - - // Then return the correct multiplier, since m_dTimeStep is in hours - switch (nTimeUnits) - { - case TIME_UNKNOWN: - return TIME_UNKNOWN; - break; - - case TIME_HOURS: - return 1; // Multiplier for hours - break; - - case TIME_DAYS: - return 24; // Multiplier for days -> hours - break; - - case TIME_MONTHS: - return 24 * 30.416667; // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years) - break; - - case TIME_YEARS: - return 24 * 365.25; // Multiplier for years -> hours - break; - } - - return 0; -} - -//=============================================================================================================================== -//! Given a string containing time units, this sets up the appropriate multiplier and display units for the simulation -//=============================================================================================================================== -int CSimulation::nDoSimulationTimeMultiplier(string const* strIn) -{ - // First decide what the time units are - int const nTimeUnits = nDoTimeUnits(strIn); - - // Next set up the correct multiplier, since m_dTimeStep is in hours - switch (nTimeUnits) - { - case TIME_UNKNOWN: - return RTN_ERR_TIME_UNITS; - break; - - case TIME_HOURS: - m_dDurationUnitsMult = 1; // Multiplier for hours - m_strDurationUnits = "hours"; - break; - - case TIME_DAYS: - m_dDurationUnitsMult = 24; // Multiplier for days -> hours - m_strDurationUnits = "days"; - break; - - case TIME_MONTHS: - m_dDurationUnitsMult = 24 * 30.416667; // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years) - m_strDurationUnits = "months"; - break; - - case TIME_YEARS: - m_dDurationUnitsMult = 24 * 365.25; // Multiplier for years -> hours - m_strDurationUnits = "years"; - break; - } - - return RTN_OK; -} - -//=============================================================================================================================== -//! This finds time units in a string -//=============================================================================================================================== -int CSimulation::nDoTimeUnits(string const* strIn) -{ - if (strIn->find("hour") != string::npos) - return TIME_HOURS; - - else if (strIn->find("day") != string::npos) - return TIME_DAYS; - - else if (strIn->find("month") != string::npos) - return TIME_MONTHS; - - else if (strIn->find("year") != string::npos) - return TIME_YEARS; - - else - return TIME_UNKNOWN; -} - -//=============================================================================================================================== -//! Opens the log file -//=============================================================================================================================== -bool CSimulation::bOpenLogFile(void) -{ - if (m_nLogFileDetail == 0) - { - LogStream.open("/dev/null", ios::out | ios::trunc); - cout << "Warning: log file is not writting" << endl; - } - - else - LogStream.open(m_strLogFile.c_str(), ios::out | ios::trunc); - - if (!LogStream) - { - // Error, cannot open log file - cerr << ERR << "cannot open " << m_strLogFile << " for output" << endl; - return false; - } - - return true; -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the DEM file -//=============================================================================================================================== -void CSimulation::AnnounceReadBasementDEM(void) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_BASEMENT << pstrChangeToForwardSlash(&m_strInitialBasementDEMFile) << endl; -#else - cout << READING_BASEMENT << m_strInitialBasementDEMFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now allocating memory -//=============================================================================================================================== -void CSimulation::AnnounceAllocateMemory(void) -{ - cout << ALLOCATE_MEMORY << endl; -} - -//=============================================================================================================================== -//! Tells the user that we are now adding layers -//=============================================================================================================================== -void CSimulation::AnnounceAddLayers(void) -{ - // Tell the user what is happening - cout << ADD_LAYERS << endl; -} - -//=============================================================================================================================== -//! Now reading raster GIS files -//=============================================================================================================================== -void CSimulation::AnnounceReadRasterFiles(void) -{ - cout << READING_RASTER_FILES << endl; -} - -//=============================================================================================================================== -//! Now reading vector GIS files -//=============================================================================================================================== -void CSimulation::AnnounceReadVectorFiles(void) -{ - cout << READING_VECTOR_FILES << endl; -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the Landscape category GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadLGIS(void) const -{ - // Tell the user what is happening - if (! m_strInitialLandformFile.empty()) -#ifdef _WIN32 - cout << READING_LANDFORM_FILE << pstrChangeToForwardSlash(&m_strInitialLandformFile) << endl; - -#else - cout << READING_LANDFORM_FILE << m_strInitialLandformFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the Intervention class GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadICGIS(void) const -{ - // Tell the user what is happening - if (! m_strInterventionClassFile.empty()) -#ifdef _WIN32 - cout << READING_INTERVENTION_CLASS_FILE << pstrChangeToForwardSlash(&m_strInterventionClassFile) << endl; - -#else - cout << READING_INTERVENTION_CLASS_FILE << m_strInterventionClassFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the Intervention height GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadIHGIS(void) const -{ - // Tell the user what is happening - if (! m_strInterventionHeightFile.empty()) -#ifdef _WIN32 - cout << READING_INTERVENTION_HEIGHT_FILE << pstrChangeToForwardSlash(&m_strInterventionHeightFile) << endl; - -#else - cout << READING_INTERVENTION_HEIGHT_FILE << m_strInterventionHeightFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the deep water wave values GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadDeepWaterWaveValuesGIS(void) const -{ - // Tell the user what is happening - if (! m_strDeepWaterWavesInputFile.empty()) -#ifdef _WIN32 - cout << READING_DEEP_WATER_WAVE_FILE << pstrChangeToForwardSlash(&m_strDeepWaterWavesInputFile) << endl; - -#else - cout << READING_DEEP_WATER_WAVE_FILE << m_strDeepWaterWavesInputFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the sediment input events GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadSedimentEventInputValuesGIS(void) const -{ - // Tell the user what is happening - if (! m_strSedimentInputEventFile.empty()) -#ifdef _WIN32 - cout << READING_SED_INPUT_EVENT_FILE << pstrChangeToForwardSlash(&m_strSedimentInputEventFile) << endl; - -#else - cout << READING_SED_INPUT_EVENT_FILE << m_strSedimentInputEventFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the flood location GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadFloodLocationGIS(void) const -{ - // Tell the user what is happening - if (! m_strFloodLocationShapefile.empty()) -#ifdef _WIN32 - cout << READING_FLOOD_LOCATION << pstrChangeToForwardSlash(&m_strFloodLocationShapefile) << endl; - -#else - cout << READING_FLOOD_LOCATION << m_strFloodLocationShapefile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial suspended sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialSuspSedGIS(void) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_SUSPENDED_SEDIMENT_FILE << pstrChangeToForwardSlash(&m_strInitialSuspSedimentFile) << endl; -#else - cout << READING_SUSPENDED_SEDIMENT_FILE << m_strInitialSuspSedimentFile << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial fine unconsolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialFineUnconsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_UNCONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialFineUnconsSedimentFile[nLayer]) << endl; -#else - cout << READING_UNCONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialFineUnconsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial sand unconsolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialSandUnconsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_UNCONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialSandUnconsSedimentFile[nLayer]) << endl; -#else - cout << READING_UNCONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialSandUnconsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial coarse unconsolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialCoarseUnconsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_UNCONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialCoarseUnconsSedimentFile[nLayer]) << endl; -#else - cout << READING_UNCONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialCoarseUnconsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial fine consolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialFineConsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_CONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialFineConsSedimentFile[nLayer]) << endl; -#else - cout << READING_CONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialFineConsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial sand consolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialSandConsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_CONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialSandConsSedimentFile[nLayer]) << endl; -#else - cout << READING_CONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialSandConsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Tells the user that we are now reading the initial coarse consolidated sediment depth GIS file -//=============================================================================================================================== -void CSimulation::AnnounceReadInitialCoarseConsSedGIS(int const nLayer) const -{ - // Tell the user what is happening -#ifdef _WIN32 - cout << READING_CONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialCoarseConsSedimentFile[nLayer]) << endl; -#else - cout << READING_CONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialCoarseConsSedimentFile[nLayer] << endl; -#endif -} - -//=============================================================================================================================== -//! Now reading tide data file -//=============================================================================================================================== -void CSimulation::AnnounceReadTideData(void) const -{ -#ifdef _WIN32 - cout << READING_TIDE_DATA_FILE << pstrChangeToForwardSlash(&m_strTideDataFile) << endl; -#else - cout << READING_TIDE_DATA_FILE << m_strTideDataFile << endl; -#endif -} - -//=============================================================================================================================== -//! Now reading the SCAPE shape function file -//=============================================================================================================================== -void CSimulation::AnnounceReadSCAPEShapeFunctionFile(void) -{ - cout << READING_SCAPE_SHAPE_FUNCTION_FILE << endl; -} - -//=============================================================================================================================== -//! Tells the user that we are now initializing -//=============================================================================================================================== -void CSimulation::AnnounceFinalInitialization(void) -{ - // Tell the user what is happening - cout << INITIALIZING_FINAL << endl; -} - -//=============================================================================================================================== -//! Tell the user that the simulation is now running -//=============================================================================================================================== -void CSimulation::AnnounceIsRunning(void) -{ - cout << RUN_NOTICE << endl; -} - -//=============================================================================================================================== -//! Return a space-separated string containing the names of the raster GIS output files -//=============================================================================================================================== -string CSimulation::strListRasterFiles(void) const -{ - string strTmp; - - if (m_bBasementElevSave) - { - strTmp.append(RASTER_BASEMENT_ELEVATION_CODE); - strTmp.append(", "); - } - - if (m_bSedIncTalusTopSurfSave) - { - strTmp.append(RASTER_SEDIMENT_TOP_CODE); - strTmp.append(", "); - } - - if (m_bTopSurfIncSeaSave) - { - strTmp.append(RASTER_TOP_ELEVATION_INC_SEA_CODE); - strTmp.append(", "); - } - - if (m_bTalusSave) - { - strTmp.append(RASTER_TALUS_CODE); - strTmp.append(", "); - } - - if (m_bSeaDepthSave) - { - strTmp.append(RASTER_SEA_DEPTH_CODE); - strTmp.append(", "); - } - - if (m_bAvgSeaDepthSave) - { - strTmp.append(RASTER_AVG_SEA_DEPTH_CODE); - strTmp.append(", "); - } - - if (m_bSeaMaskSave) - { - strTmp.append(RASTER_INUNDATION_MASK_CODE); - strTmp.append(", "); - } - - if (m_bWaveHeightSave) - { - strTmp.append(RASTER_WAVE_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bWaveAngleSave) - { - strTmp.append(RASTER_WAVE_ORIENTATION_CODE); - strTmp.append(", "); - } - - if (m_bAvgWaveHeightSave) - { - strTmp.append(RASTER_AVG_WAVE_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bBeachProtectionSave) - { - strTmp.append(RASTER_BEACH_PROTECTION_CODE); - strTmp.append(", "); - } - - if (m_bPotentialPlatformErosionSave) - { - strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bPotentialPlatformErosionMaskSave) - { - strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE); - strTmp.append(", "); - } - - if (m_bBeachDepositionSave) - { - strTmp.append(RASTER_BEACH_DEPOSITION_CODE); - strTmp.append(", "); - } - - if (m_bTotalBeachDepositionSave) - { - strTmp.append(RASTER_TOTAL_BEACH_DEPOSITION_CODE); - strTmp.append(", "); - } - - if (m_bBeachMaskSave) - { - strTmp.append(RASTER_BEACH_MASK_CODE); - strTmp.append(", "); - } - - if (m_bActualPlatformErosionSave) - { - strTmp.append(RASTER_ACTUAL_PLATFORM_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bTotalPotentialPlatformErosionSave) - { - strTmp.append(RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bTotalActualPlatformErosionSave) - { - strTmp.append(RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bPotentialBeachErosionSave) - { - strTmp.append(RASTER_POTENTIAL_BEACH_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bActualBeachErosionSave) - { - strTmp.append(RASTER_ACTUAL_BEACH_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bTotalPotentialBeachErosionSave) - { - strTmp.append(RASTER_TOTAL_POTENTIAL_BEACH_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bTotalActualBeachErosionSave) - { - strTmp.append(RASTER_TOTAL_ACTUAL_BEACH_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bLandformSave) - { - strTmp.append(RASTER_LANDFORM_CODE); - strTmp.append(", "); - } - - if (m_bSlopeConsSedSave) - { - strTmp.append(RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_CODE); - strTmp.append(", "); - } - - if (m_bInterventionClassSave) - { - strTmp.append(RASTER_INTERVENTION_CLASS_CODE); - strTmp.append(", "); - } - - if (m_bInterventionHeightSave) - { - strTmp.append(RASTER_INTERVENTION_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bHaveFineSediment && m_bSuspSedSave) - { - strTmp.append(RASTER_SUSP_SED_CODE); - strTmp.append(", "); - } - - if (m_bHaveFineSediment && m_bAvgSuspSedSave) - { - strTmp.append(RASTER_AVG_SUSP_SED_CODE); - strTmp.append(", "); - } - - if (m_bHaveFineSediment && m_bFineUnconsSedSave) - { - strTmp.append(RASTER_FINE_UNCONS_CODE); - strTmp.append(", "); - } - - if (m_bHaveSandSediment && m_bSandUnconsSedSave) - { - strTmp.append(RASTER_SAND_UNCONS_CODE); - strTmp.append(", "); - } - - if (m_bHaveCoarseSediment && m_bCoarseUnconsSedSave) - { - strTmp.append(RASTER_COARSE_UNCONS_CODE); - strTmp.append(", "); - } - - if (m_bHaveFineSediment && m_bFineConsSedSave) - { - strTmp.append(RASTER_FINE_CONS_CODE); - strTmp.append(", "); - } - - if (m_bHaveSandSediment && m_bSandConsSedSave) - { - strTmp.append(RASTER_SAND_CONS_CODE); - strTmp.append(", "); - } - - if (m_bHaveCoarseSediment && m_bCoarseConsSedSave) - { - strTmp.append(RASTER_COARSE_CONS_CODE); - strTmp.append(", "); - } - - if (m_bRasterCoastlineSave) - { - strTmp.append(RASTER_COAST_CODE); - strTmp.append(", "); - } - - if (m_bRasterNormalProfileSave) - { - strTmp.append(RASTER_COAST_NORMAL_CODE); - strTmp.append(", "); - } - - if (m_bActiveZoneSave) - { - strTmp.append(RASTER_ACTIVE_ZONE_CODE); - strTmp.append(", "); - } - - if (m_bRasterPolygonSave) - { - strTmp.append(RASTER_POLYGON_CODE); - strTmp.append(", "); - } - - if (m_bPotentialPlatformErosionMaskSave) - { - strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE); - strTmp.append(", "); - } - - if (m_bSedimentInputEventSave) - { - strTmp.append(RASTER_SEDIMENT_INPUT_EVENT_CODE); - strTmp.append(", "); - } - - // Remove the trailing comma and space - if (strTmp.size() > 2) - strTmp.resize(strTmp.size() - 2); - - return strTmp; -} - -//=============================================================================================================================== -//! Return a space-separated string containing the names of the vector GIS output files -//=============================================================================================================================== -string CSimulation::strListVectorFiles(void) const -{ - string strTmp; - - if (m_bCoastSave) - { - strTmp.append(VECTOR_COAST_CODE); - strTmp.append(", "); - } - - if (m_bNormalsSave) - { - strTmp.append(VECTOR_NORMALS_CODE); - strTmp.append(", "); - } - - if (m_bInvalidNormalsSave) - { - strTmp.append(VECTOR_INVALID_NORMALS_CODE); - strTmp.append(", "); - } - - if (m_bWaveAngleAndHeightSave) - { - strTmp.append(VECTOR_WAVE_ANGLE_AND_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bAvgWaveAngleAndHeightSave) - { - strTmp.append(VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bCoastCurvatureSave) - { - strTmp.append(VECTOR_COAST_CURVATURE_CODE); - strTmp.append(", "); - } - - if (m_bWaveEnergySinceCollapseSave) - { - strTmp.append(VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_CODE); - strTmp.append(", "); - } - - if (m_bMeanWaveEnergySave) - { - strTmp.append(VECTOR_MEAN_WAVE_ENERGY_CODE); - strTmp.append(", "); - } - - if (m_bBreakingWaveHeightSave) - { - strTmp.append(VECTOR_BREAKING_WAVE_HEIGHT_CODE); - strTmp.append(", "); - } - - if (m_bPolygonNodeSave) - { - strTmp.append(VECTOR_POLYGON_NODE_CODE); - strTmp.append(", "); - } - - if (m_bPolygonBoundarySave) - { - strTmp.append(VECTOR_POLYGON_BOUNDARY_CODE); - strTmp.append(", "); - } - - if (m_bCliffNotchSave) - { - strTmp.append(VECTOR_CLIFF_NOTCH_ACTIVE_CODE); - strTmp.append(", "); - } - - if (m_bShadowBoundarySave) - { - strTmp.append(VECTOR_SHADOW_ZONE_BOUNDARY_CODE); - strTmp.append(", "); - } - - if (m_bShadowDowndriftBoundarySave) - { - strTmp.append(VECTOR_DOWNDRIFT_ZONE_BOUNDARY_CODE); - strTmp.append(", "); - } - - if (m_bDeepWaterWaveAngleAndHeightSave) - { - strTmp.append(VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_CODE); - strTmp.append(", "); - } - - // Remove the trailing comma and space - if (strTmp.size() > 2) - strTmp.resize(strTmp.size() - 2); - - return strTmp; -} - -//=============================================================================================================================== -//! Return a space-separated string containing the names of the time series output files -//=============================================================================================================================== -string CSimulation::strListTSFiles(void) const -{ - string strTmp; - - if (m_bSeaAreaTSSave) - { - strTmp.append(TIME_SERIES_SEA_AREA_CODE); - strTmp.append(", "); - } - - if (m_bSWLTSSave) - { - strTmp.append(TIME_SERIES_SWL_CODE); - strTmp.append(", "); - } - - if (m_bActualPlatformErosionTSSave) - { - strTmp.append(TIME_SERIES_PLATFORM_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bCliffCollapseErosionTSSave) - { - strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bCliffCollapseDepositionTSSave) - { - strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_CODE); - strTmp.append(", "); - } - - if (m_bCliffCollapseNetTSSave) - { - strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_NET_CODE); - strTmp.append(", "); - } - - if (m_bBeachErosionTSSave) - { - strTmp.append(TIME_SERIES_BEACH_EROSION_CODE); - strTmp.append(", "); - } - - if (m_bBeachDepositionTSSave) - { - strTmp.append(TIME_SERIES_BEACH_DEPOSITION_CODE); - strTmp.append(", "); - } - - if (m_bBeachSedimentChangeNetTSSave) - { - strTmp.append(TIME_SERIES_BEACH_CHANGE_NET_CODE); - strTmp.append(", "); - } - - if (m_bSuspSedTSSave) - { - strTmp.append(TIME_SERIES_SUSPENDED_SEDIMENT_CODE); - strTmp.append(", "); - } - - if (m_bFloodSetupSurgeTSSave) - { - strTmp.append(TIME_SERIES_FLOOD_SETUP_SURGE_CODE); - strTmp.append(", "); - } - - if (m_bFloodSetupSurgeRunupTSSave) - { - strTmp.append(TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE); - strTmp.append(", "); - } - - if (m_bCliffNotchElevTSSave) - { - strTmp.append(TIME_SERIES_CLIFF_NOTCH_ELEV_CODE); - strTmp.append(", "); - } - - // Remove the trailing comma and space - if (strTmp.size() > 2) - strTmp.resize(strTmp.size() - 2); - - return strTmp; -} - -//=============================================================================================================================== -//! This member function intialises the time series files -//=============================================================================================================================== -bool CSimulation::bSetUpTSFiles(void) -{ - string strTSFile; - - if (m_bSeaAreaTSSave) - { - // Start with wetted area - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_SEA_AREA_NAME); - strTSFile.append(CSVEXT); - - // Open sea area time-series CSV file - SeaAreaTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! SeaAreaTSStream) - { - // Error, cannot open wetted area time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bSWLTSSave) - { - // Now SWL - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_SWL_NAME); - strTSFile.append(CSVEXT); - - // Open SWL time-series CSV file - SWLTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! SWLTSStream) - { - // Error, cannot open SWL time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bActualPlatformErosionTSSave) - { - // Erosion (fine, sand, coarse) - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_PLATFORM_EROSION_NAME); - strTSFile.append(CSVEXT); - - // Open erosion time-series CSV file - PlatformErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! PlatformErosionTSStream) - { - // Error, cannot open erosion time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bCliffCollapseErosionTSSave) - { - // Erosion due to cliff collapse - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_EROSION_NAME); - strTSFile.append(CSVEXT); - - // Open cliff collapse erosion time-series CSV file - CliffCollapseErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! CliffCollapseErosionTSStream) - { - // Error, cannot open cliff collapse erosion time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bCliffCollapseDepositionTSSave) - { - // Deposition due to cliff collapse - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_NAME); - strTSFile.append(CSVEXT); - - // Open cliff collapse deposition time-series CSV file - CliffCollapseDepositionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! CliffCollapseDepositionTSStream) - { - // Error, cannot open cliff collapse deposition time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bCliffCollapseNetTSSave) - { - // Net change in unconsolidated sediment due to cliff collapse - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_NET_NAME); - strTSFile.append(CSVEXT); - - // Open net cliff collapse time-series CSV file - CliffCollapseNetChangeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! CliffCollapseNetChangeTSStream) - { - // Error, cannot open net cliff collapse time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bBeachErosionTSSave) - { - // Beach erosion - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_BEACH_EROSION_NAME); - strTSFile.append(CSVEXT); - - // Open beach erosion time-series CSV file - BeachErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! BeachErosionTSStream) - { - // Error, cannot open beach erosion time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bBeachDepositionTSSave) - { - // Beach deposition - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_BEACH_DEPOSITION_NAME); - strTSFile.append(CSVEXT); - - // Open beach deposition time-series CSV file - BeachDepositionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! BeachDepositionTSStream) - { - // Error, cannot open beach deposition time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bBeachSedimentChangeNetTSSave) - { - // Beach sediment change - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_BEACH_CHANGE_NET_NAME); - strTSFile.append(CSVEXT); - - // Open net beach sediment change time-series CSV file - BeachSedimentNetChangeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! BeachSedimentNetChangeTSStream) - { - // Error, cannot open beach sediment change time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bSuspSedTSSave) - { - // Sediment load - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_SUSPENDED_SEDIMENT_NAME); - strTSFile.append(CSVEXT); - - // Open sediment load time-series CSV file - FineSedSuspensionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! FineSedSuspensionTSStream) - { - // Error, cannot open sediment load time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bFloodSetupSurgeTSSave) - { - // Sediment load - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_FLOOD_SETUP_SURGE_CODE); - strTSFile.append(CSVEXT); - - // Open sediment load time-series CSV file - FloodSetupSurgeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! FloodSetupSurgeTSStream) - { - // Error, cannot open sediment load time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bFloodSetupSurgeRunupTSSave) - { - // Sediment load - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE); - strTSFile.append(CSVEXT); - - // Open sediment load time-series CSV file - FloodSetupSurgeRunupTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! FloodSetupSurgeRunupTSStream) - { - // Error, cannot open sediment load time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - if (m_bCliffNotchElevTSSave) - { - // Elevation of cliff notch - strTSFile = m_strOutPath; - strTSFile.append(TIME_SERIES_CLIFF_NOTCH_ELEV_NAME); - strTSFile.append(CSVEXT); - - // Open cliff notch elevation time-series CSV file - CliffNotchElevTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); - if (! CliffNotchElevTSStream) - { - // Error, cannot open cliff notch elevation time-series file - cerr << ERR << "cannot open " << strTSFile << " for output" << endl; - return false; - } - } - - return true; -} - -//=============================================================================================================================== -//! Checks to see if the simulation has gone on too long, amongst other things -//=============================================================================================================================== -bool CSimulation::bTimeToQuit(void) -{ - // Add timestep to the total time simulated so far - m_dSimElapsed += m_dTimeStep; - - if (m_dSimElapsed >= m_dSimDuration) - { - // It is time to quit - m_dSimElapsed = m_dSimDuration; - AnnounceProgress(); - return true; - } - - // Not quitting, so increment the timestep count, and recalc total timesteps - m_ulIter++; - m_ulTotTimestep = static_cast(dRound(m_dSimDuration / m_dTimeStep)); - - // Check to see if we have done CLOCK_CHECK_ITERATION timesteps: if so, it is time to reset the CPU time running total in case the clock() function later rolls over - if (0 == m_ulIter % CLOCK_CHECK_ITERATION) - DoCPUClockReset(); - - // Not yet time to quit - return false; -} - -//=============================================================================================================================== -//! Returns a string, hopefully giving the name of the computer on which the simulation is running -//=============================================================================================================================== -string CSimulation::strGetComputerName(void) -{ - string strComputerName; - -#ifdef _WIN32 - // Being compiled to run under Windows, either by MS VC++, Borland C++, or Cygwin - strComputerName = getenv("COMPUTERNAME"); -#else - // Being compiled for another platform; assume for Linux-Unix - char szHostName[BUF_SIZE] = ""; - gethostname(szHostName, BUF_SIZE); - - strComputerName = szHostName; - - if (strComputerName.empty()) - strComputerName = "Unknown Computer"; - -#endif - - return strComputerName; -} - -//=============================================================================================================================== -//! Resets the CPU clock timer to prevent it 'rolling over', as can happen during long runs. This is a particularly problem under Unix systems where the value returned by clock() is defined in microseconds (for compatibility with systems that have CPU clocks with much higher resolution) i.e. CLOCKS_PER_SEC is 1000000 rather than the more usual 1000. In this case, the value returned from clock() will wrap around after accumulating only 2147 seconds of CPU time (about 36 minutes). -//=============================================================================================================================== -void CSimulation::DoCPUClockReset(void) -{ - if (static_cast(-1) == clock()) - { - // Error - LogStream << "CPU time not available" << endl; - m_dCPUClock = -1; - return; - } - - // OK, so carry on - double dClkThis = static_cast(clock()); - dClkThis -= CLOCK_T_MIN; // necessary when clock_t is signed, to make dClkThis unsigned - - if (dClkThis < m_dClkLast) - { - // Clock has 'rolled over' - m_dCPUClock += (CLOCK_T_RANGE + 1 - m_dClkLast); // this elapsed before rollover - m_dCPUClock += dClkThis; // this elapsed after rollover - -#ifdef CLOCKCHECK - // For debug purposes - LogStream << "Rolled over: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << endl - << "\t" - << " before rollover=" << (CLOCK_T_RANGE + 1 - m_dClkLast) << endl - << "\t" - << " after rollover=" << dClkThis << endl - << "\t" - << " ADDED=" << (CLOCK_T_RANGE + 1 - m_dClkLast + dClkThis) << endl; -#endif - } - - else - { - // No rollover - m_dCPUClock += (dClkThis - m_dClkLast); - -#ifdef CLOCKCHECK - // For debug purposes - LogStream << "No rollover: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << " ADDED=" << dClkThis - m_dClkLast << endl; -#endif - } - - // Reset for next time - m_dClkLast = dClkThis; -} - -//=============================================================================================================================== -//! Announce the end of the simulation -//=============================================================================================================================== -void CSimulation::AnnounceSimEnd(void) -{ - cout << endl << FINAL_OUTPUT << endl; -} - -// //=============================================================================================================================== -// //! Calculates and displays time elapsed in terms of CPU time and real time, also calculates time per timestep in terms of both CPU time and real time -// //=============================================================================================================================== -// void CSimulation::CalcTime(double const dRunLength) -// { -// // Reset CPU count for last time -// DoCPUClockReset(); -// -// if (! bFPIsEqual(m_dCPUClock, -1.0, TOLERANCE)) -// { -// // Calculate CPU time in secs -// double const dDuration = m_dCPUClock / CLOCKS_PER_SEC; -// -// // And write CPU time out to OutStream and LogStream -// OutStream << "CPU time elapsed: " << strDispTime(dDuration, false, true); -// LogStream << "CPU time elapsed: " << strDispTime(dDuration, false, true); -// -// // Calculate CPU time per timestep -// double const dPerTimestep = dDuration / static_cast(m_ulTotTimestep); -// -// // And write CPU time per timestep to OutStream and LogStream -// OutStream << fixed << setprecision(4) << " (" << dPerTimestep << " per timestep)" << endl; -// LogStream << fixed << setprecision(4) << " (" << dPerTimestep << " per timestep)" << endl; -// -// // Calculate ratio of CPU time to time simulated -// OutStream << resetiosflags(ios::floatfield); -// OutStream << fixed << setprecision(0) << "In terms of CPU time, this is "; -// LogStream << resetiosflags(ios::floatfield); -// LogStream << fixed << setprecision(0) << "In terms of CPU time, this is "; -// -// if (dDuration > dRunLength) -// { -// OutStream << dDuration / dRunLength << " x slower than reality" << endl; -// LogStream << dDuration / dRunLength << " x slower than reality" << endl; -// } -// -// else -// { -// OutStream << dRunLength / dDuration << " x faster than reality" << endl; -// LogStream << dRunLength / dDuration << " x faster than reality" << endl; -// } -// } -// -// // Calculate run time -// double const dDuration = difftime(m_tSysEndTime, m_tSysStartTime); -// -// // And write run time out to OutStream and LogStream -// OutStream << "Run time elapsed: " << strDispTime(dDuration, false, false); -// LogStream << "Run time elapsed: " << strDispTime(dDuration, false, false); -// -// // Calculate run time per timestep -// double const dPerTimestep = dDuration / static_cast(m_ulTotTimestep); -// -// // And write run time per timestep to OutStream and LogStream -// OutStream << resetiosflags(ios::floatfield); -// OutStream << " (" << fixed << setprecision(4) << dPerTimestep << " per timestep)" << endl; -// LogStream << resetiosflags(ios::floatfield); -// LogStream << " (" << fixed << setprecision(4) << dPerTimestep << " per timestep)" << endl; -// -// // Calculate ratio of run time to time simulated -// OutStream << fixed << setprecision(0) << "In terms of run time, this is "; -// LogStream << fixed << setprecision(0) << "In terms of run time, this is "; -// -// if (dDuration > dRunLength) -// { -// OutStream << dDuration / dRunLength << " x slower than reality" << endl; -// LogStream << dDuration / dRunLength << " x slower than reality" << endl; -// } -// -// else -// { -// OutStream << dRunLength / dDuration << " x faster than reality" << endl; -// LogStream << dRunLength / dDuration << " x faster than reality" << endl; -// } -// } - -//=============================================================================================================================== -//! strDispSimTime returns a string formatted as year Julian_day hour, given a parameter in hours -//=============================================================================================================================== -string CSimulation::strDispSimTime(const double dTimeIn) -{ - // Make sure no negative times - double dTmpTime = tMax(dTimeIn, 0.0); - - string strTime; - - // Constants - double const dHoursInYear = 24 * 365; // it was 365.25 - double const dHoursInDay = 24; - - // Display years - if (dTmpTime >= dHoursInYear) - { - double const dYears = floor(dTmpTime / dHoursInYear); - dTmpTime -= (dYears * dHoursInYear); - - strTime = to_string(static_cast(dYears)); - strTime.append("y "); - } - else - strTime = "0y "; - - // Display Julian days - if (dTmpTime >= dHoursInDay) - { - double const dJDays = floor(dTmpTime / dHoursInDay); - dTmpTime -= (dJDays * dHoursInDay); - - stringstream ststrTmp; - ststrTmp << FillToWidth('0', 3) << static_cast(dJDays); - strTime.append(ststrTmp.str()); - strTime.append("d "); - } - else - strTime.append("000d "); - - // Display hours - stringstream ststrTmp; - ststrTmp << FillToWidth('0', 2) << static_cast(dTmpTime); - strTime.append(ststrTmp.str()); - strTime.append("h"); - - return strTime; -} - -//=============================================================================================================================== -//! strDispTime returns a string formatted as h:mm:ss, given a parameter in seconds, with rounding and fractions of a second if desired -//=============================================================================================================================== -string CSimulation::strDispTime(const double dTimeIn, const bool bRound, const bool bFrac) -{ - // Make sure no negative times - double dTime = tMax(dTimeIn, 0.0); - - string strTime; - - if (bRound) - dTime = dRound(dTime); - - unsigned long ulTimeIn = static_cast(floor(dTime)); - dTime -= static_cast(ulTimeIn); - - // Hours - if (ulTimeIn >= 3600) - { - // Display some hours - unsigned long const ulHours = ulTimeIn / 3600ul; - ulTimeIn -= (ulHours * 3600ul); - - strTime = to_string(ulHours); - strTime.append(":"); - } - else - strTime = "0:"; - - // Minutes - if (ulTimeIn >= 60) - { - // display some minutes - unsigned long const ulMins = ulTimeIn / 60ul; - ulTimeIn -= (ulMins * 60ul); - - stringstream ststrTmp; - ststrTmp << FillToWidth('0', 2) << ulMins; - strTime.append(ststrTmp.str()); - strTime.append(":"); - } - else - strTime.append("00:"); - - // Seconds - stringstream ststrTmp; - ststrTmp << FillToWidth('0', 2) << ulTimeIn; - strTime.append(ststrTmp.str()); - - if (bFrac) - { - // Fractions of a second - strTime.append("."); - ststrTmp.clear(); - ststrTmp.str(string()); - ststrTmp << FillToWidth('0', 2) << static_cast(dTime * 100); - strTime.append(ststrTmp.str()); - } - - return strTime; -} - -//=============================================================================================================================== -//! Returns the date and time on which the program was compiled -//=============================================================================================================================== -string CSimulation::strGetBuild(void) -{ - string strBuild("("); - strBuild.append(__TIME__); - strBuild.append(" "); - strBuild.append(__DATE__); -#ifdef _DEBUG - strBuild.append(" DEBUG"); -#endif - strBuild.append(" build)"); - - return strBuild; -} - -//=============================================================================================================================== -//! Displays information regarding the progress of the simulation -//=============================================================================================================================== -void CSimulation::AnnounceProgress(void) -{ - if (isatty(fileno(stdout))) - { - // Stdout is connected to a tty, so not running as a background job - static double sdElapsed = 0; - static double sdToGo = 0; - time_t const tNow = time(nullptr); - - // Calculate time elapsed and remaining - sdElapsed = difftime(tNow, m_tSysStartTime); - sdToGo = (sdElapsed * m_dSimDuration / m_dSimElapsed) - sdElapsed; - - // Tell the user about progress (note need to make several separate calls to cout here, or MS VC++ compiler appears to get confused) - cout << SIMULATING << strDispSimTime(m_dSimElapsed); - cout << fixed << setprecision(3) << setw(9) << 100 * m_dSimElapsed / m_dSimDuration; - cout << "% (elapsed " << strDispTime(sdElapsed, false, false) << " remaining "; - - cout << strDispTime(sdToGo, false, false) << ") "; - - // Add a 'marker' for GIS saves etc. - if (m_bSaveGISThisIter) - cout << setw(9) << "GIS" + to_string(m_nGISSave); - else if (m_bSedimentInputThisIter) - cout << setw(9) << "SED INPUT"; - else - cout << setw(9) << SPACE; - - cout.flush(); - } -} - -//=============================================================================================================================== -//! This calculates and displays process statistics -//=============================================================================================================================== -void CSimulation::CalcProcessStats(void) -{ - string const NA = "Not available"; - - OutStream << endl; - OutStream << "Process statistics" << endl; - OutStream << "------------------" << endl; - -#ifdef _WIN32 - // First, find out which version of Windows we are running under - OSVERSIONINFOEX osvi; - BOOL bOsVersionInfoEx; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); // fill this much memory with zeros - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*)&osvi))) - { - // OSVERSIONINFOEX didn't work so try OSVERSIONINFO instead - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - if (!GetVersionEx((OSVERSIONINFO*)&osvi)) - { - // That didn't work either, too risky to proceed so give up - OutStream << NA << endl; - return; - } - } - - // OK, we have Windows version so display it - OutStream << "Running under \t: "; - - switch (osvi.dwPlatformId) - { - case VER_PLATFORM_WIN32_NT: - if (osvi.dwMajorVersion <= 4) - OutStream << "Windows NT "; - - else if (5 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) - OutStream << "Windows 2000 "; - - else if (5 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion) - OutStream << "Windows XP "; - - else if (6 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) - OutStream << "Windows Vista "; - - else if (6 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion) - OutStream << "Windows 7 "; - - else if (6 == osvi.dwMajorVersion && 2 == osvi.dwMinorVersion) - OutStream << "Windows 8 "; - - else if (6 == osvi.dwMajorVersion && 3 == osvi.dwMinorVersion) - OutStream << "Windows 8.1 "; - - else if (10 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) - OutStream << "Windows 10 "; - - else if (11 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) - OutStream << "Windows 11 "; - - else - OutStream << "unknown Windows version "; - - // Display version, service pack (if any), and build number - if (osvi.dwMajorVersion <= 4) - OutStream << "version " << osvi.dwMajorVersion << "." << osvi.dwMinorVersion << " " << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl; - - else - OutStream << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl; - - break; - - case VER_PLATFORM_WIN32_WINDOWS: - if (4 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) - { - OutStream << "Windows 95"; - - if ('C' == osvi.szCSDVersion[1] || 'B' == osvi.szCSDVersion[1]) - OutStream << " OSR2"; - - OutStream << endl; - } - - else if (4 == osvi.dwMajorVersion && 10 == osvi.dwMinorVersion) - { - OutStream << "Windows 98"; - - if ('A' == osvi.szCSDVersion[1]) - OutStream << "SE"; - - OutStream << endl; - } - - else if (4 == osvi.dwMajorVersion && 90 == osvi.dwMinorVersion) - OutStream << "Windows Me" << endl; - - else - OutStream << "unknown 16-bit Windows version " << endl; - - break; - - case VER_PLATFORM_WIN32s: - OutStream << "Win32s" << endl; - break; - } - - // Now get process timimgs: this only works under 32-bit windows - if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId) - { - FILETIME ftCreate, ftExit, ftKernel, ftUser; - - if (GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser)) - { - ULARGE_INTEGER ul; - ul.LowPart = ftUser.dwLowDateTime; - ul.HighPart = ftUser.dwHighDateTime; - OutStream << "Time spent executing user code \t: " << strDispTime(static_cast(ul.QuadPart) * 1e-7, false) << endl; - ul.LowPart = ftKernel.dwLowDateTime; - ul.HighPart = ftKernel.dwHighDateTime; - OutStream << "Time spent executing kernel code \t: " << strDispTime(static_cast(ul.QuadPart) * 1e-7, false) << endl; - } - } - - else - OutStream << "Process timings \t: " << NA << endl; - - // Finally get more process statistics: this needs psapi.dll, so only proceed if it is present on this system - HINSTANCE hDLL = LoadLibrary("psapi.dll"); - - if (hDLL != NULL) - { - // The dll has been found - typedef BOOL(__stdcall * DLLPROC)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD); - DLLPROC ProcAdd; - - // Try to get the address of the function we will call - ProcAdd = (DLLPROC)GetProcAddress(hDLL, "GetProcessMemoryInfo"); - - if (ProcAdd) - { - // Address was found - PROCESS_MEMORY_COUNTERS pmc; - - // Now call the function - if ((ProcAdd)(GetCurrentProcess(), &pmc, sizeof(pmc))) - { - OutStream << "Peak working set size \t: " << pmc.PeakWorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Current working set size \t: " << pmc.WorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Peak paged pool usage \t: " << pmc.QuotaPeakPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Current paged pool usage \t: " << pmc.QuotaPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Peak non-paged pool usage \t: " << pmc.QuotaPeakNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Current non-paged pool usage \t: " << pmc.QuotaNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Peak pagefile usage \t: " << pmc.PeakPagefileUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "Current pagefile usage \t: " << pmc.PagefileUsage / (1024.0 * 1024.0) << " Mb" << endl; - OutStream << "No. of page faults \t: " << pmc.PageFaultCount << endl; - } - } - - // Free the memory used by the dll - FreeLibrary(hDLL); - } - -#elif defined __GNUG__ - rusage ru; - - if (getrusage(RUSAGE_SELF, &ru) >= 0) - { - OutStream << "Time spent executing user code \t: " << strDispTime(static_cast(ru.ru_utime.tv_sec), false, true) << endl; - // OutStream << "ru_utime.tv_usec \t: " << ru.ru_utime.tv_usec << endl; - OutStream << "Time spent executing kernel code \t: " << strDispTime(static_cast(ru.ru_stime.tv_sec), false, true) << endl; - // OutStream << "ru_stime.tv_usec \t: " << ru.ru_stime.tv_usec << endl; - // OutStream << "Maximum resident set size \t: " << ru.ru_maxrss/1024.0 << " Mb" << endl; - // OutStream << "ixrss (???) \t: " << ru.ru_ixrss << endl; - // OutStream << "Sum of rm_asrss (???) \t: " << ru.ru_idrss << endl; - // OutStream << "isrss (???) \t: " << ru.ru_isrss << endl; - OutStream << "No. of page faults not requiring physical I/O\t: " << ru.ru_minflt << endl; - OutStream << "No. of page faults requiring physical I/O \t: " << ru.ru_majflt << endl; - // OutStream << "No. of times swapped out of main memory \t: " << ru.ru_nswap << endl; - // OutStream << "No. of times performed input (read request) \t: " << ru.ru_inblock << endl; - // OutStream << "No. of times performed output (write request)\t: " << ru.ru_oublock << endl; - // OutStream << "No. of signals received \t: " << ru.ru_nsignals << endl; - OutStream << "No. of voluntary context switches \t: " << ru.ru_nvcsw << endl; - OutStream << "No. of involuntary context switches \t: " << ru.ru_nivcsw << endl; - } - - else - OutStream << NA << endl; - -#else - OutStream << NA << endl; -#endif - - OutStream << endl; - -#ifdef _OPENMP -#pragma omp parallel - { - if (0 == omp_get_thread_num()) - { - OutStream << "Number of OpenMP threads \t: " << omp_get_num_threads() << endl; - OutStream << "Number of OpenMP processors \t: " << omp_get_num_procs() << endl; - - LogStream << "Number of OpenMP threads \t: " << omp_get_num_threads() << endl; - LogStream << "Number of OpenMP processors \t: " << omp_get_num_procs() << endl; - } - } -#endif - - time_t const tRunTime = m_tSysEndTime - m_tSysStartTime; - struct tm* ptmRunTime = gmtime(&tRunTime); - - OutStream << "Time required for simulation \t: " << put_time(ptmRunTime, "%T") << endl; - LogStream << "Time required for simulation \t: " << put_time(ptmRunTime, "%T") << endl; - - double const dSpeedUp = m_dSimDuration * 3600 / static_cast(tRunTime); - OutStream << setprecision(0); - OutStream << "Time simulated / time required for simulation\t: " << dSpeedUp << " x faster than reality" << endl; - - LogStream << setprecision(0); - LogStream << "Time simulated / time required for simulation\t: " << dSpeedUp << " x faster than reality" << endl; -} - -//=============================================================================================================================== -//! Returns an error message given an error code -//=============================================================================================================================== -string CSimulation::strGetErrorText(int const nErr) -{ - string strErr; - - switch (nErr) - { - case RTN_USER_ABORT: - strErr = "run ended by user"; - break; - - case RTN_ERR_BADPARAM: - strErr = "error in command-line parameter"; - break; - - case RTN_ERR_INI: - strErr = "error reading initialisation file"; - break; - - case RTN_ERR_CMEDIR: - strErr = "error in directory name"; - break; - - case RTN_ERR_RUNDATA: - strErr = "error reading run details file"; - break; - - case RTN_ERR_SCAPE_SHAPE_FUNCTION_FILE: - strErr = "error reading SCAPE shape function file"; - break; - - case RTN_ERR_TIDEDATAFILE: - strErr = "error reading tide data file"; - break; - - case RTN_ERR_LOGFILE: - strErr = "error creating log file"; - break; - - case RTN_ERR_OUTFILE: - strErr = "error creating text output file"; - break; - - case RTN_ERR_TSFILE: - strErr = "error creating time series file"; - break; - - case RTN_ERR_DEMFILE: - strErr = "error reading initial DEM file"; - break; - - case RTN_ERR_RASTER_FILE_READ: - strErr = "error reading raster GIS file"; - break; - - case RTN_ERR_VECTOR_FILE_READ: - strErr = "error reading vector GIS file"; - break; - - case RTN_ERR_MEMALLOC: - strErr = "error allocating memory"; - break; - - case RTN_ERR_RASTER_GIS_OUT_FORMAT: - strErr = "problem with raster GIS output format"; - break; - - case RTN_ERR_VECTOR_GIS_OUT_FORMAT: - strErr = "problem with vector GIS output format"; - break; - - case RTN_ERR_TEXT_FILE_WRITE: - strErr = "error writing text output file"; - break; - - case RTN_ERR_RASTER_FILE_WRITE: - strErr = "error writing raster GIS output file"; - break; - - case RTN_ERR_VECTOR_FILE_WRITE: - strErr = "error writing vector GIS output file"; - break; - - case RTN_ERR_TIMESERIES_FILE_WRITE: - strErr = "error writing time series output file"; - break; - - case RTN_ERR_LINETOGRID: - strErr = "error putting linear feature onto raster grid"; - break; - - case RTN_ERR_NOSEACELLS: - strErr = "no sea cells found"; - break; - - case RTN_ERR_GRID_TO_LINE: - strErr = "error when searching grid for linear feature"; - break; - - case RTN_ERR_NO_COAST: - strErr = "no coastlines found. Is the SWL correct?"; - break; - - case RTN_ERR_PROFILE_WRITE: - strErr = "error writing coastline-normal profiles"; - break; - - case RTN_ERR_TIME_UNITS: - strErr = "error in time units"; - break; - - case RTN_ERR_NO_SOLUTION_FOR_ENDPOINT: - strErr = "no solution when finding end point for coastline-normal line"; - break; - - case RTN_ERR_PROFILE_ENDPOINT_IS_INLAND: - strErr = "end point for coastline-normal line is not in the contiguous sea"; - break; - - case RTN_ERR_CLIFF_NOTCH: - strErr = "cliff notch is above sediment top elevation"; - break; - - case RTN_ERR_CLIFF_CANNOT_DEPOSIT_ALL: - strErr = "unable to deposit enough unconsolidated sediment (talus) from cliff collapse"; - break; - - case RTN_ERR_PROFILE_SPACING: - strErr = "coastline-normal profiles are too closely spaced"; - break; - - case RTN_ERR_NO_PROFILES_1: - strErr = "no coastline-normal profiles created, check the SWL"; - break; - - case RTN_ERR_NO_PROFILES_2: - strErr = "no coastline-normal profiles created during rasterization"; - break; - - case RTN_ERR_EDGE_OF_GRID: - strErr = "hit grid edge when eroding beach"; - break; - - case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_BEACH_EROSION: - strErr = "could not locate seaward end of profile when creating Dean profile for beach erosion"; - break; - - case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_UPCOAST_BEACH_DEPOSITION: - strErr = "could not locate seaward end of profile when creating Dean profile for up-coast beach deposition"; - break; - - case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_DOWNCOAST_BEACH_DEPOSITION: - strErr = "could not locate seaward end of profile when creating Dean profile for down-coast beach deposition"; - break; - - case RTN_ERR_LANDFORM_TO_GRID: - strErr = "updating grid with landforms"; - break; - - case RTN_ERR_NO_TOP_LAYER: - strErr = "no top layer of sediment"; - break; - - case RTN_ERR_NO_ADJACENT_POLYGON: - strErr = "problem with polygon-to-polygon sediment routing sequence"; - break; - - case RTN_ERR_BAD_MULTILINE: - strErr = "inconsistent multiline"; - break; - - case RTN_ERR_CANNOT_INSERT_POINT: - strErr = "cannot insert point into multiline"; - break; - - case RTN_ERR_CANNOT_ASSIGN_COASTAL_LANDFORM: - strErr = "cannot assign coastal landform"; - break; - - case RTN_ERR_SHADOW_ZONE_FLOOD_FILL_NOGRID: - strErr = "start point for cell-by-cell fill of wave shadow zone is outside grid"; - break; - - case RTN_ERR_SHADOW_ZONE_FLOOD_START_POINT: - strErr = "could not find start point for cell-by-cell fill of wave shadow zone"; - break; - - case RTN_ERR_CSHORE_EMPTY_PROFILE: - strErr = "empty profile during during CShore wave propagation"; - break; - - case RTN_ERR_CSHORE_FILE_INPUT: - strErr = "creating file for CShore input"; - break; - - case RTN_ERR_READING_CSHORE_FILE_OUTPUT: - strErr = "reading CShore output file"; - break; - - case RTN_ERR_WAVE_INTERPOLATION_LOOKUP: - strErr = "during wave interpolation lookup"; - break; - - case RTN_ERR_GRIDCREATE: - strErr = "while running GDALGridCreate()"; - break; - - case RTN_ERR_COAST_CANT_FIND_EDGE_CELL: - strErr = "cannot find edge cell while constructing grid-edge profile"; - break; - - case RTN_ERR_CSHORE_ERROR: - strErr = "CShore did not finish correctly"; - break; - - case RTN_ERR_NO_CELL_UNDER_COASTLINE: - strErr = "Could not find cell under coastline"; - break; - - case RTN_ERR_OPEN_DEEP_WATER_WAVE_DATA: - strErr = "opening deep sea wave time series file"; - break; - - case RTN_ERR_READING_DEEP_WATER_WAVE_DATA: - strErr = "reading deep sea wave time series file"; - break; - - case RTN_ERR_BOUNDING_BOX: - strErr = "finding edges of the bounding box"; - break; - - case RTN_ERR_READING_SEDIMENT_INPUT_EVENT: - strErr = "reading sediment input event time series file"; - break; - - case RTN_ERR_SEDIMENT_INPUT_EVENT: - strErr = "simulating sediment input event"; - break; - - case RTN_ERR_SEDIMENT_INPUT_EVENT_LOCATION: - strErr = "location of sediment input event is outside grod"; - break; - - case RTN_ERR_WAVESTATION_LOCATION: - strErr = "location of wavestation is outside grid"; - break; - - case RTN_ERR_CLIFF_NOT_IN_POLYGON: - strErr = "cliff not in polygon"; - break; - - case RTN_ERR_CELL_MARKED_PROFILE_COAST_BUT_NOT_PROFILE: - strErr = "Cell marked as profile coast but not as profile"; - break; - - case RTN_ERR_TRACING_FLOOD: - strErr = "error tracing flood line on grid"; - break; - - case RTN_ERR_NO_START_FINISH_POINTS_TRACING_COAST: - strErr = "error tracing coastline on grid, no coast start-finish points found"; - break; - - case RTN_ERR_NO_VALID_COAST: - strErr = "error tracing coastline on grid, no valid coast found"; - break; - - case RTN_ERR_REPEATING_WHEN_TRACING_COAST: - strErr = "error tracing coastline on grid, coast search just repeats"; - break; - - case RTN_ERR_ZERO_LENGTH_COAST: - strErr = "error tracing coastline on grid, zero-length coast found"; - break; - - case RTN_ERR_COAST_TOO_SMALL: - strErr = "error tracing coastline on grid, coast below minimum permitted length"; - break; - - case RTN_ERR_IGNORING_COAST: - strErr = "error tracing coastline on grid, coast ignored"; - break; - - case RTN_ERR_TOO_LONG_TRACING_COAST: - strErr = "error tracing coastline on grid, too many times round tracing loop"; - break; - - case RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE_DIFFERENT_COASTS: - strErr = "intersection cell not found in hit profile"; - break; - - case RTN_ERR_POINT_NOT_FOUND_IN_MULTILINE_DIFFERENT_COASTS: - strErr = "point not found when truncating multiline for different coasts"; - break; - - case RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE: - strErr = "cell not found in hit profile"; - break; - - case RTN_ERR_CELL_IN_POLY_BUT_NO_POLY_COAST: - strErr = "cell marked as in polygon, but does not have polygon's coast"; - break; - - case RTN_ERR_CLIFF_TALUS_TO_UNCONS: - strErr = "cannot find closest point to coast when moving talus to unconsolidated sediment"; - break; - - case RTN_ERR_UNKNOWN: - strErr = "unknown error"; - break; - - default: - // should never get here - strErr = " error"; - } - - return strErr; -} - -//=============================================================================================================================== -//! Notifies the user that the simulation has ended, asks for keypress if necessary, and if compiled under GNU can send an email -//=============================================================================================================================== -void CSimulation::DoSimulationEnd(int const nRtn) -{ - // If we don't know the time that the run ended (e.g. because it did not finish correctly), then get it now - if (m_tSysEndTime == 0) - m_tSysEndTime = time(nullptr); - - switch (nRtn) - { - case (RTN_OK): - // normal ending - cout << RUN_END_NOTICE << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; - break; - - case (RTN_HELP_ONLY): - case (RTN_CHECK_ONLY): - return; - - default: - // Aborting because of some error - cerr << RUN_END_NOTICE << "iteration " << m_ulIter << ERROR_NOTICE << nRtn << ": \"" << strGetErrorText(nRtn) << "\", " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; - - if (m_ulIter > 1) - { - // If the run has actually started, then output all GIS files: this is very helpful in tracking down problems - m_bSaveGISThisIter = true; - m_nGISSave = 998; // Will get incremented to 999 when we write the files - bSaveAllRasterGISFiles(); - bSaveAllVectorGISFiles(); - } - - // Write the error message to the logfile and to stdout - if (LogStream && LogStream.is_open()) - { - LogStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; - LogStream.flush(); - } - - if (OutStream && OutStream.is_open()) - { - OutStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; - OutStream.flush(); - } - } - -#ifdef __GNUG__ - if (isatty(fileno(stdout))) - { - // Stdout is connected to a tty, so not running as a background job - // cout << endl - // << PRESS_KEY; - // cout.flush(); - // getchar(); - } - else - { - // Stdout is not connected to a tty, so must be running in the background; if we have something entered for the email address, then send an email - if (! m_strMailAddress.empty()) - { - cout << SEND_EMAIL << m_strMailAddress << endl; - - string strCmd("echo \""); - - stringstream ststrTmp; - ststrTmp << put_time(localtime(&m_tSysEndTime), "%T on %A %d %B %Y") << endl; - - // Send an email using Linux/Unix mail command - if (RTN_OK == nRtn) - { - // Finished normally - strCmd.append("Simulation "); - strCmd.append(m_strRunName); - strCmd.append(", running on "); - strCmd.append(strGetComputerName()); - strCmd.append(", completed normally at "); - strCmd.append(ststrTmp.str()); - strCmd.append("\" | mail -s \""); - strCmd.append(PROGRAM_NAME); - strCmd.append(": normal completion\" "); - strCmd.append(m_strMailAddress); - } - else - { - // Error, so give some information to help debugging - strCmd.append("Simulation "); - strCmd.append(m_strRunName); - strCmd.append(", running on "); - strCmd.append(strGetComputerName()); - strCmd.append(", aborted with error code "); - strCmd.append(to_string(nRtn)); - strCmd.append(": "); - strCmd.append(strGetErrorText(nRtn)); - strCmd.append(" at timestep "); - strCmd.append(to_string(m_ulIter)); - strCmd.append(" ("); - strCmd.append(strDispSimTime(m_dSimElapsed)); - strCmd.append(").\n\nThis message sent at "); - strCmd.append(ststrTmp.str()); - strCmd.append("\" | mail -s \""); - strCmd.append(PROGRAM_NAME); - strCmd.append(": ERROR\" "); - strCmd.append(m_strMailAddress); - } - - int const nRet = system(strCmd.c_str()); - - if (WEXITSTATUS(nRet) != 0) - cerr << ERR << EMAIL_ERROR << endl; - } - } -#endif -} - -//=============================================================================================================================== -//! Changes all forward slashes in the input string to backslashes, leaving the original unchanged -//=============================================================================================================================== -string CSimulation::pstrChangeToBackslash(string const* strIn) -{ - string strOut(*strIn); - strOut.replace(strOut.begin(), strOut.end(), '/', '\\'); - return strOut; -} - -//=============================================================================================================================== -//! Swaps all backslashes in the input string to forward slashes, leaving the original unchanged -//=============================================================================================================================== -string CSimulation::pstrChangeToForwardSlash(string const* strIn) -{ - string strOut(*strIn); - strOut.replace(strOut.begin(), strOut.end(), '\\', '/'); - return strOut; -} - -//=============================================================================================================================== -//! Trims whitespace from the left side of a string, does not change the original string -//=============================================================================================================================== -string CSimulation::strTrimLeft(string const* strIn) -{ - // Trim leading spaces - size_t const nStartpos = strIn->find_first_not_of(" \t"); - - if (nStartpos == string::npos) - return *strIn; - - else - return strIn->substr(nStartpos); -} - -//=============================================================================================================================== -//! Trims whitespace from the right side of a string, does not change the original string -//=============================================================================================================================== -string CSimulation::strTrimRight(string const* strIn) -{ - string strTmp(*strIn); - - // Remove any stray carriage returns (can happen if file was edited in Windows) - strTmp.erase(remove(strTmp.begin(), strTmp.end(), '\r'), strTmp.end()); - - // Trim trailing spaces - size_t const nEndpos = strTmp.find_last_not_of(" \t"); - - if (nEndpos == string::npos) - return strTmp; - - else - return strTmp.substr(0, nEndpos + 1); -} - -//=============================================================================================================================== -//! Trims whitespace from both sides of a string, does not change the original string -//=============================================================================================================================== -string CSimulation::strTrim(string const* strIn) -{ - string strTmp = *strIn; - - // Remove any stray carriage returns (can happen if file was edited in Windows) - strTmp.erase(remove(strTmp.begin(), strTmp.end(), '\r'), strTmp.end()); - - // Trim trailing spaces - size_t nPos = strTmp.find_last_not_of(" \t"); - - if (nPos != string::npos) - strTmp.resize(nPos + 1); - - // Trim leading spaces - nPos = strTmp.find_first_not_of(" \t"); - - if (nPos != string::npos) - strTmp = strTmp.substr(nPos); - - return strTmp; -} - -//=============================================================================================================================== -//! Returns the lower case version of an string, leaving the original unchanged -//=============================================================================================================================== -string CSimulation::strToLower(string const* strIn) -{ - string strOut = *strIn; - transform(strIn->begin(), strIn->end(), strOut.begin(), tolower); - return strOut; -} - -//=============================================================================================================================== -// Returns the upper case version of an string, leaving the original unchanged -//=============================================================================================================================== -// string CSimulation::strToUpper(string const* strIn) -// { -// string strOut = *strIn; -// transform(strIn->begin(), strIn->end(), strOut.begin(), toupper); -// return strOut; -// } - -//=============================================================================================================================== -//! Returns a string with a substring removed, and with whitespace trimmed -//=============================================================================================================================== -string CSimulation::strRemoveSubstr(string* pStrIn, string const* pStrSub) -{ - size_t const nPos = pStrIn->find(*pStrSub); - - if (nPos != string::npos) - { - // OK, found the substring - pStrIn->replace(nPos, pStrSub->size(), ""); - return strTrim(pStrIn); - } - - else - { - // If not found, return the string unchanged - return *pStrIn; - } -} - -//=============================================================================================================================== -//! From http://stackoverflow.com/questions/236129/split-a-string-in-c They implement (approximately) Python's split() function. This first version puts the results into a pre-constructed string vector. It ignores empty items -//=============================================================================================================================== -vector* CSimulation::VstrSplit(string const* s, char const delim, vector* elems) -{ - stringstream ss(*s); - string item; - - while (getline(ss, item, delim)) - { - if (!item.empty()) - elems->push_back(item); - } - - return elems; -} - -//=============================================================================================================================== -//! From http://stackoverflow.com/questions/236129/split-a-string-in-c They implement (approximately) Python's split() function. This second version returns a new string vector (it calls the first version) -//=============================================================================================================================== -vector CSimulation::VstrSplit(string const* s, char const delim) -{ - vector elems; - VstrSplit(s, delim, &elems); - return elems; -} - -// //=============================================================================================================================== -// //! Calculates the vector cross product of three points -// //=============================================================================================================================== -// double CSimulation::dCrossProduct(double const dX1, double const dY1, double const dX2, double const dY2, double const dX3, double const dY3) -// { -// // Based on code at http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/clockwise.htm -// return (dX2 - dX1) * (dY3 - dY2) - ((dY2 - dY1) * (dX3 - dX2)); -// } - -// //=============================================================================================================================== -// //! Calculates the mean of a pointer to a vector of doubles -// //=============================================================================================================================== -// double CSimulation::dGetMean(vector const* pV) -// { -// double dSum = accumulate(pV->begin(), pV->end(), 0.0); -// double dMean = dSum / static_cast(pV->size()); -// return dMean; -// } - -// //=============================================================================================================================== -// //! Calculates the standard deviation of a pointer to a vector of doubles. From http://stackoverflow.com/questions/7616511/calculate-mean-and-standard-deviation-from-a-vector-of-samples-in-c-using-boos -// //=============================================================================================================================== -// double CSimulation::dGetStdDev(vector const* pV) -// { -// double dSum = accumulate(pV->begin(), pV->end(), 0.0); -// double dMean = dSum / static_cast(pV->size()); -// -// double dSqSum = inner_product(pV->begin(), pV->end(), pV->begin(), 0.0); -// double dStdDev = sqrt(dSqSum / static_cast(pV->size()) - dMean * dMean); -// -// return dStdDev; -// } - -//=============================================================================================================================== -//! Appends a CGeom2DIPoint to a vector, making sure that the new end point touches the previous end point i.e. that there is no gap between the two points -//=============================================================================================================================== -void CSimulation::AppendEnsureNoGap(vector* pVPtiPoints, CGeom2DIPoint const* pPti) -{ - int const nX = pPti->nGetX(); - int const nY = pPti->nGetY(); - int const nXLast = pVPtiPoints->back().nGetX(); - int const nYLast = pVPtiPoints->back().nGetY(); - int const nXDiff = nX - nXLast; - int const nYDiff = nY - nYLast; - int const nXDiffA = tAbs(nXDiff); - int const nYDiffA = tAbs(nYDiff); - int const nDiff = tMax(nXDiffA, nYDiffA); - - if (nDiff > 1) - { - // We have a gap - double - dXInc = 0, - dYInc = 0; - - if (nXDiffA > 1) - dXInc = static_cast(nXDiff) / nDiff; - - if (nYDiffA > 1) - dYInc = static_cast(nYDiff) / nDiff; - - for (int n = 1; n < nDiff; n++) - { - CGeom2DIPoint const Pti(nXLast + nRound(n * dXInc), nYLast + nRound(n * dYInc)); - pVPtiPoints->push_back(Pti); - } - } - - pVPtiPoints->push_back(CGeom2DIPoint(nX, nY)); -} - -//=============================================================================================================================== -//! Calculates a Dean equilibrium profile h(y) = A * y^(2/3) where h(y) is the distance below the highest point in the Dean profile at a distance y from the landward start of the profile -//=============================================================================================================================== -void CSimulation::CalcDeanProfile(vector* pdVDeanProfile, double const dInc, double const dDeanTopElev, double const dA, bool const bDeposition, int const nSeawardOffset, double const dStartCellElev) -{ - double dDistFromProfileStart = 0; - - if (bDeposition) - { - // This Dean profile is for deposition i.e. seaward displacement of the profile - pdVDeanProfile->at(0) = dStartCellElev; // Is coast elevation - - for (int n = 1; n < static_cast(pdVDeanProfile->size()); n++) - { - if (n <= nSeawardOffset) - // As we extend the profile seaward, the elevation of any points coastward of the new coast point of the Dean profile are set to the elevation of the original coast point - pdVDeanProfile->at(n) = dStartCellElev; - else - { - double const dDistBelowTop = dA * pow(dDistFromProfileStart, DEAN_POWER); - pdVDeanProfile->at(n) = dDeanTopElev - dDistBelowTop; - - dDistFromProfileStart += dInc; - } - } - } - else - { - // This Dean profile is for erosion i.e. landward displacement of the profile - for (int n = 0; n < static_cast(pdVDeanProfile->size()); n++) - { - double const dDistBelowTop = dA * pow(dDistFromProfileStart, DEAN_POWER); - pdVDeanProfile->at(n) = dDeanTopElev - dDistBelowTop; - - dDistFromProfileStart += dInc; - } - } -} - -//=============================================================================================================================== -//! Calculate the total elevation difference between every point in two elevation profiles (first profile - second profile) -//=============================================================================================================================== -double CSimulation::dSubtractProfiles(vector const* pdVFirstProfile, vector const* pdVSecondProfile, vector const* pbVIsValid) -{ - double dTotElevDiff = 0; - - // Note that this assumes that all three vectors are of equal length, should really check this - for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) - { - if (pbVIsValid->at(n)) - { - double const dProfileDiff = pdVFirstProfile->at(n) - pdVSecondProfile->at(n); - - dTotElevDiff += dProfileDiff; - } - } - - // // DEBUG CODE ----------------------------------------------------- - // LogStream << endl; - // LogStream << "First profile = "; - // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) - // { - // LogStream << pdVFirstProfile->at(n) << " "; - // } - // LogStream << endl; - // LogStream << "Second profile = "; - // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) - // { - // LogStream << pdVSecondProfile->at(n) << " "; - // } - // LogStream << endl; - // LogStream << "Difference = "; - // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) - // { - // LogStream << pdVFirstProfile->at(n) - pdVSecondProfile->at(n) << " "; - // } - // LogStream << endl; - // // DEBUG CODE ----------------------------------------------------- - - return dTotElevDiff; -} - -//=============================================================================================================================== -//! Calculate the depth of closure -//=============================================================================================================================== -void CSimulation::CalcDepthOfClosure(void) -{ - double - dDeepWaterWaveHeight, - dDeepWaterPeriod; - - if (m_bSingleDeepWaterWaveValues) - { - dDeepWaterWaveHeight = m_dAllCellsDeepWaterWaveHeight; - dDeepWaterPeriod = m_dAllCellsDeepWaterWavePeriod; - } - - else - { - dDeepWaterWaveHeight = m_dMaxUserInputWaveHeight; - dDeepWaterPeriod = m_dMaxUserInputWavePeriod; - } - - // TODO 051 Calculate depth of closure using 'average of the maximum values observed during a typical year' - // dL = 2.28 * Hsx − (68.5 * Hsx^2 / (g * Tsx^2)) - // where: - // Hsx is the nearshore storm wave height that is exceeded only 12 hours each year - // Tsx is the associated wave period - // from Hallermeier, R.J. (1978). Uses for a calculated limit depth to beach erosion. Proc. 16th Coastal Engineering Conf., ASCE, New York. Pp 1493 - 1512 - // - // For the time being, and since we assume wave height and period constant just use the actual wave height and period to calculate the depth of closure - // m_dDepthOfClosure = (2.28 * dDeepWaterWaveHeight) - (68.5 * dDeepWaterWaveHeight * dDeepWaterWaveHeight / (m_dG * dDeepWaterPeriod * dDeepWaterPeriod)); - - // An alternative (which produces smaller depth of closure estimates) is Birkemeier (1985) TODO 007 Full reference needed - // dL = 1.75 * Hsx - (57.9 * Hsx^2/ (g * Tsx^2)) - m_dDepthOfClosure = (1.75 * dDeepWaterWaveHeight) - (57.9 * dDeepWaterWaveHeight * dDeepWaterWaveHeight / (m_dG * dDeepWaterPeriod * dDeepWaterPeriod)); -} - -// //=============================================================================================================================== -// //! Tests a reference to a string to see if it is numeric (modified from https://tfetimes.com/c-determine-if-a-string-is-numeric/) -// //=============================================================================================================================== -// bool CSimulation::bIsNumeric(string const*strIn) -// { -// return all_of(strIn->begin(), strIn->end(), isdigit); -// } - -//=============================================================================================================================== -//! Parses a date string into days, months, and years, and checks each of them -//=============================================================================================================================== -bool CSimulation::bParseDate(string const* strDate, int& nDay, int& nMonth, int& nYear) -{ - vector VstrTmp = VstrSplit(strDate, SLASH); - - if (VstrTmp.size() < 3) - { - cerr << "date string must include day, month, and year '" << strDate << "'" << endl; - return false; - } - - // Sort out day - if (! bIsStringValidInt(VstrTmp[0])) - { - cerr << "invalid integer for day in date '" << strDate << "'" << endl; - return false; - } - - nDay = stoi(VstrTmp[0]); - - if ((nDay < 1) || (nDay > 31)) - { - cerr << "day must be between 1 and 31 in date '" << strDate << "'" << endl; - return false; - } - - // Sort out month - if (! bIsStringValidInt(VstrTmp[1])) - { - cerr << "invalid integer for month in date '" << strDate << "'" << endl; - return false; - } - - nMonth = stoi(VstrTmp[1]); - - if ((nMonth < 1) || (nMonth > 12)) - { - cerr << "month must be between 1 and 12 in date '" << strDate << "'" << endl; - return false; - } - - // Sort out year - if (! bIsStringValidInt(VstrTmp[2])) - { - cerr << "invalid integer for year in date '" << strDate << "'" << endl; - return false; - } - - nYear = stoi(VstrTmp[2]); - - if (nYear < 0) - { - cerr << "year must be > 0 in date '" << strDate << "'" << endl; - return false; - } - - return true; -} - -//=============================================================================================================================== -//! Parses a time string into hours, minutes, and seconds, and checks each of them -//=============================================================================================================================== -bool CSimulation::bParseTime(string const* strTime, int& nHour, int& nMin, int& nSec) -{ - vector VstrTmp = VstrSplit(strTime, DASH); - - if (VstrTmp.size() < 3) - { - cerr << "time string must include hours, minutes, and seconds '" << strTime << "'" << endl; - return false; - } - - // Sort out hour - if (! bIsStringValidInt(VstrTmp[0])) - { - cerr << "invalid integer for hours in time '" << strTime << "'" << endl; - return false; - } - - nHour = stoi(VstrTmp[0]); - - if ((nHour < 0) || (nHour > 23)) - { - cerr << "hour must be between 0 and 23 in time '" << strTime << "'" << endl; - return false; - } - - // Sort out minutes - if (! bIsStringValidInt(VstrTmp[1])) - { - cerr << "invalid integer for minutes in time '" << strTime << "'" << endl; - return false; - } - - nMin = stoi(VstrTmp[1]); - - if ((nMin < 0) || (nMin > 59)) - { - cerr << "minutes must be betwen 0 and 59 in time '" << strTime << "'" << endl; - return false; - } - - // Sort out seconds - if (! bIsStringValidInt(VstrTmp[2])) - { - cerr << "invalid integer for seconds in time '" << strTime << "'" << endl; - return false; - } - - nSec = stoi(VstrTmp[2]); - - if ((nSec < 0) || (nSec > 59)) - { - cerr << "seconds must be between 0 and 59 in time '" << strTime << "'" << endl; - return false; - } - - return true; -} - -//=============================================================================================================================== -//! For sediment input events, parses a string that may be relative (a number of hours or days after the start of the simulation), or absolute (a time/date in the format hh-mm-ss dd/mm/yyyy). Returns the timestep in which the sediment input event occurs -//=============================================================================================================================== -unsigned long CSimulation::ulConvertToTimestep(string const* pstrIn) const -{ - unsigned long ulTimeStep = 0; - - // Convert to lower case, remove leading and trailing whitespace - string strDate = strToLower(pstrIn); - strDate = strTrim(&strDate); - - if (strDate.find("hour") != string::npos) - { - // OK, this is a number of hours (a relative time, from the start of simulation) - vector VstrTmp = VstrSplit(&strDate, SPACE); - - if ((VstrTmp.size() < 2) || (! bIsStringValidInt(VstrTmp[0]))) - { - cerr << "Error in number of hours '" + strDate + "' for sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - double const dHours = stod(strTrim(&VstrTmp[0])); - - if (dHours > m_dSimDuration) - { - cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - ulTimeStep = static_cast(dRound(dHours / m_dTimeStep)); - } - - else if (strDate.find("day") != string::npos) - { - // OK, this is a number of days (a relative time, from the start of simulation) - vector VstrTmp = VstrSplit(&strDate, SPACE); - - if ((VstrTmp.size() < 2) || (! bIsStringValidInt(VstrTmp[0]))) - { - cerr << "Error in number of days '" + strDate + "' for sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - double const dHours = stod(strTrim(&VstrTmp[0])) * 24; - - if (dHours > m_dSimDuration) - { - cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - ulTimeStep = static_cast(dRound(dHours / m_dTimeStep)); - } - - else - { - // This is an absolute time/date in the format hh-mm-ss dd/mm/yyyy - vector VstrTmp = VstrSplit(&strDate, SPACE); - - if (VstrTmp.size() < 2) - { - cerr << "Error in time/date '" + strDate + "' of sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - int nHour = 0; - int nMin = 0; - int nSec = 0; - - // OK, first sort out the time - if (! bParseTime(&VstrTmp[0], nHour, nMin, nSec)) - { - cerr << "Error in time '" + VstrTmp[0] + "' of sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - int nDay = 0; - int nMonth = 0; - int nYear = 0; - - // Now sort out the time - if (! bParseDate(&VstrTmp[1], nDay, nMonth, nYear)) - { - cerr << "Error in date '" + VstrTmp[1] + "' of sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - // This is modified from https://stackoverflow.com/questions/14218894/number-of-days-between-two-dates-c - struct tm tmSimStart = {}; - tmSimStart.tm_sec = m_nSimStartSec; - tmSimStart.tm_min = m_nSimStartMin; - tmSimStart.tm_hour = m_nSimStartHour; - tmSimStart.tm_mday = m_nSimStartDay; - tmSimStart.tm_mon = m_nSimStartMonth - 1; - tmSimStart.tm_year = m_nSimStartYear - 1900; - - struct tm tmSimEvent = {}; - tmSimEvent.tm_sec = nSec; - tmSimEvent.tm_min = nMin; - tmSimEvent.tm_hour = nHour; - tmSimEvent.tm_mday = nDay; - tmSimEvent.tm_mon = nMonth - 1; - tmSimEvent.tm_year = nYear - 1900; - - time_t const tStart = mktime(&tmSimStart); - time_t const tEvent = mktime(&tmSimEvent); - - if (tStart == (time_t)(-1)) - { - cerr << "Error in simulation start time/date" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - if (tEvent == (time_t)(-1)) - { - cerr << "Error in time/date '" + strDate + "' of sediment input event" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - double const dHours = difftime(tEvent, tStart) / (60 * 60); - - if (dHours < 0) - { - cerr << "Sediment input event '" + strDate + "' occurs before start of simulation" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - if (dHours > m_dSimDuration) - { - cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; - return SEDIMENT_INPUT_EVENT_ERROR; - } - - ulTimeStep = static_cast(dHours / m_dTimeStep); - } - - return ulTimeStep; -} - -//=============================================================================================================================== -//! Returns true if the cell is an intervention -//=============================================================================================================================== -bool CSimulation::bIsInterventionCell(int const nX, int const nY) const -{ - int const nCat = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); - if ((nCat == LF_INTERVENTION_STRUCT) || (nCat == LF_INTERVENTION_NON_STRUCT)) - return true; - - return false; -} - -//=============================================================================================================================== -//! Do end-of-run memory clearance -//=============================================================================================================================== -void CSimulation::DoEndOfRunDeletes(void) -{ - // Clear all vector coastlines, profiles, and polygons - m_VCoast.clear(); - - // m_VFloodWaveSetup.clear(); - m_VFloodWaveSetupSurge.clear(); - m_VFloodWaveSetupSurgeRunup.clear(); -} - -//=============================================================================================================================== -//! Calculate Mean High Water (MHW) elevation for a given duration (in days). This is a tidal datum determined from the arithmetic mean of the high water heights observed each tidal day -//=============================================================================================================================== -void CSimulation::CalcMHWElevation(int const nTideDataCount) -{ - // Calculate the number of tide values to read - int const nTidevaluesPerDay = tMax(nRound(24 / m_dTimeStep), 1); - int const nTideValuesToRead = nTidevaluesPerDay * NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC; - - int const nNumTideValues = static_cast(m_VdTideData.size()); - - // Now read the tide data (note that this assumes that the first line of the tide data is the first tide reading of the day) - int nThisCount = nTideDataCount; - double dTotMaxTide = 0; - for (int n = 0; n < nTideValuesToRead; n++) - { - // Read in this days's tide data - double dDayMaxTide = -DBL_MAX; - for (int m = 0; m < nTidevaluesPerDay; m++) - { - // If necessary, wrap the tide data, i.e. start again with the first line of the tide data if we do not have enough - if (nThisCount > nNumTideValues - 1) - nThisCount = 0; - - double const dThisTideData = m_VdTideData[nThisCount]; - - if (dThisTideData > dDayMaxTide) - dDayMaxTide = dThisTideData; - - nThisCount++; - } - - // We have the max tide for the day, so increment the total of highest daily tides - dTotMaxTide += dDayMaxTide; - } - - // Now calculate the average max tide for the next NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC days - double const dMaxTideAvg = dTotMaxTide / nTideValuesToRead; - - // Finally, calculate MHW for this iteration (includes long-term SWL change) - m_dThisIterMHWElev = m_dThisIterMeanSWL + dMaxTideAvg; - - // And set the apex elevation of any new cliff notches (i.e. cliff notches which will be created during this timestep) to be at or slightly above MHW level - m_dThisIterNewNotchApexElev = m_dThisIterMHWElev + m_dNotchApexAboveMHW; - - // LogStream << m_ulIter << ": this-iteration MHW elevation = " << m_dThisIterMHWElev << " elevation of apex of new cliff notches = " << m_dThisIterNewNotchApexElev << endl; -} +/*! + \file utils.cpp + \brief Utility routines + \details TODO 001 A more detailed description of this routine. + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License +*/ + +/* ============================================================================================================================== + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +==============================================================================================================================*/ +#include + +#ifdef _WIN32 +#include // Needed for CalcProcessStats() +#include +#include // For isatty() +#elif defined __GNUG__ +#include // Needed for CalcProcessStats() +#include // For isatty() +#include +#endif + +#ifdef _OPENMP +#include +#endif + +#include + +#include + +#include + +#include +using std::tolower; + +#include +using std::floor; + +#include +using std::clock; +using std::clock_t; +using std::difftime; +using std::localtime; +using std::time; + +#include +using std::fixed; + +#include +using std::cerr; +using std::cout; +using std::endl; +using std::ios; + +#include +using std::put_time; +using std::setprecision; +using std::setw; + +#include +using std::to_string; + +#include +using std::stringstream; + +#include +using std::transform; + +#include + +#include "cme.h" +#include "simulation.h" +#include "coast.h" +#include "2di_point.h" + +//=============================================================================================================================== +//! Returns the missing value code for double precision +//=============================================================================================================================== +double CSimulation::dGetMissingValue(void) const +{ + return m_dMissingValue; +} + +//=============================================================================================================================== +//! Returns the still water level (SWL) +//=============================================================================================================================== +double CSimulation::dGetThisIterSWL(void) const +{ + return m_dThisIterSWL; +} + +//=============================================================================================================================== +//! Returns the this-iteration total water level TODO 007 Finish surge and runup stuff +//=============================================================================================================================== +double CSimulation::dGetThisIterTotWaterLevel(void) const +{ + return m_dThisIterDiffTotWaterLevel; +} + +// //=============================================================================================================================== +// //! Returns the max elevation of the beach above SWL +// //=============================================================================================================================== +// double CSimulation::dGetMaxBeachElevAboveSWL (void) const +// { +// return m_dMaxBeachElevAboveSWL; +// } + +//=============================================================================================================================== +// Returns the cell side length +//=============================================================================================================================== +// double CSimulation::dGetCellSide(void) const +// { +// return m_dCellSide; +// } + +//=============================================================================================================================== +//! Returns X grid max +//=============================================================================================================================== +int CSimulation::nGetGridXMax(void) const +{ + return m_nXGridSize; +} + +//=============================================================================================================================== +//! Returns Y grid max +//=============================================================================================================================== +int CSimulation::nGetGridYMax(void) const +{ + return m_nYGridSize; +} + +//=============================================================================================================================== +//! Returns D50 for fine sediment +//=============================================================================================================================== +double CSimulation::dGetD50Fine(void) const +{ + return m_dD50Fine; +} + +//=============================================================================================================================== +//! Returns D50 for sand sediment +//=============================================================================================================================== +double CSimulation::dGetD50Sand(void) const +{ + return m_dD50Sand; +} + +//=============================================================================================================================== +//! Returns D50 for coarse sediment +//=============================================================================================================================== +double CSimulation::dGetD50Coarse(void) const +{ + return m_dD50Coarse; +} + +//=============================================================================================================================== +//! Handles command-line parameters +//=============================================================================================================================== +int CSimulation::nHandleCommandLineParams(int nArg, char const* pcArgv[]) +{ + if ((!isatty(fileno(stdout))) || (!isatty(fileno(stderr)))) + // Running with stdout or stderr not a tty, so either redirected or running as a background job. Ignore all command line parameters + return RTN_OK; + + // Process the parameters following the name of the executable + for (int i = 1; i < nArg; i++) + { + string strArg = pcArgv[i]; + strArg = strTrim(&strArg); + +#ifdef _WIN32 + // Swap any forward slashes to backslashes + strArg = pstrChangeToBackslash(&strArg); +#endif + + if (strArg.find("--gdal") != string::npos) + { + // User wants to know what GDAL raster drivers are available + cout << GDAL_DRIVERS << endl + << endl; + + for (int j = 0; j < GDALGetDriverCount(); j++) + { + GDALDriverH hDriver = GDALGetDriver(j); + + string strTmp(GDALGetDriverShortName(hDriver)); + strTmp.append(" "); + strTmp.append(GDALGetDriverLongName(hDriver)); + + cout << strTmp << endl; + } + + return (RTN_HELP_ONLY); + } + + else + { + if (strArg.find("--about") != string::npos) + { + // User wants information about CoastalME + cout << ABOUT << endl; + cout << THANKS << endl; + + return (RTN_HELP_ONLY); + } + + else + { + if (strArg.find("--yaml") != string::npos) + { + // User wants to use YAML format for input datafile + m_bYamlInputFormat = true; + } + + else if (strArg.find("--home") != string::npos) + { + // Read in user defined runtime directory + // string strTmp; + + // Find the position of '=' + size_t const pos = strArg.find('='); + + // Was '=' found? + if (pos != string::npos) + { + // Yes, so get the substring after '=' and assign it to the global variable + m_strCMEIni = strArg.substr(pos + 1); + } + + else + { + // No + cout << "No '=' found in the input string" << endl; + } + + return (RTN_OK); + } + + // TODO 049 Handle other command line parameters e.g. path to .ini file, path to datafile + else + { + // Display usage information + cout << USAGE << endl; + cout << USAGE1 << endl; + cout << USAGE2 << endl; + cout << USAGE3 << endl; + cout << USAGE4 << endl; + cout << USAGE5 << endl; + cout << USAGE6 << endl; + + return (RTN_HELP_ONLY); + } + } + } + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! Tells the user that we have started the simulation +//=============================================================================================================================== +void CSimulation::AnnounceStart(void) +{ + cout << endl + << PROGRAM_NAME << " for " << PLATFORM << " " << strGetBuild() << endl; + #ifdef _OPENMP + cout << "OpenMP is ENABLED" << endl; + cout << "Max threads available: " << omp_get_max_threads() << endl; + #pragma omp parallel + { + #pragma omp single + std::cout << "Actually running with " << omp_get_num_threads() << " threads" << std::endl; + } + #else + std::cout << "OpenMP is NOT ENABLED - code will run serially!" << std::endl; + #endif // !_OPENMP +} + +//=============================================================================================================================== +//! Starts the clock ticking +//=============================================================================================================================== +void CSimulation::StartClock(void) +{ + // First start the 'CPU time' clock ticking + if (static_cast(-1) == clock()) + { + // There's a problem with the clock, but continue anyway + LogStream << NOTE << "CPU time not available" << endl; + m_dCPUClock = -1; + } + + else + { + // All OK, so get the time in m_dClkLast (this is needed to check for clock rollover on long runs) + m_dClkLast = static_cast(clock()); + m_dClkLast -= CLOCK_T_MIN; // necessary if clock_t is signed to make m_dClkLast unsigned + } + + // And now get the actual time we started + m_tSysStartTime = time(nullptr); +} + +//=============================================================================================================================== +//! Finds the folder (directory) in which the CoastalME executable is located +//=============================================================================================================================== +bool CSimulation::bFindExeDir(char const* pcArg) +{ + string strTmp; + char szBuf[BUF_SIZE] = ""; + +#ifdef _WIN32 + + if (0 != GetModuleFileName(NULL, szBuf, BUF_SIZE)) + strTmp = szBuf; + + else + // It failed, so try another approach + strTmp = pcArg; + +#else + // char* pResult = getcwd(szBuf, BUF_SIZE); // Used to use this, but what if cwd is not the same as the CoastalME dir? + + if (-1 != readlink("/proc/self/exe", szBuf, BUF_SIZE)) + strTmp = szBuf; + + else + // It failed, so try another approach + strTmp = pcArg; + +#endif + + // Neither approach has worked, so give up + if (strTmp.empty()) + return false; + + // It's OK, so trim off the executable's name + int const nPos = static_cast(strTmp.find_last_of(PATH_SEPARATOR)); + m_strCMEDir = strTmp.substr(0, nPos + 1); // Note that this must be terminated with a backslash + + return true; +} +//=============================================================================================================================== +//! Tells the user about the licence +//=============================================================================================================================== +void CSimulation::AnnounceLicence(void) +{ + cout << COPYRIGHT << endl + << endl; + cout << LINE << endl; + cout << DISCLAIMER1 << endl; + cout << DISCLAIMER2 << endl; + cout << DISCLAIMER3 << endl; + cout << DISCLAIMER4 << endl; + cout << DISCLAIMER5 << endl; + cout << DISCLAIMER6 << endl; + cout << LINE << endl + << endl; + + cout << START_NOTICE << strGetComputerName() << " at " << put_time(localtime(&m_tSysStartTime), "%T on %A %d %B %Y") << endl; + cout << INITIALIZING_NOTICE << endl; +} + +//=============================================================================================================================== +//! Given a string containing time units, this returns the appropriate multiplier +//=============================================================================================================================== +double CSimulation::dGetTimeMultiplier(string const* strIn) +{ + // First decide what the time units are + int const nTimeUnits = nDoTimeUnits(strIn); + + // Then return the correct multiplier, since m_dTimeStep is in hours + switch (nTimeUnits) + { + case TIME_UNKNOWN: + return TIME_UNKNOWN; + break; + + case TIME_HOURS: + return 1; // Multiplier for hours + break; + + case TIME_DAYS: + return 24; // Multiplier for days -> hours + break; + + case TIME_MONTHS: + return 24 * 30.416667; // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years) + break; + + case TIME_YEARS: + return 24 * 365.25; // Multiplier for years -> hours + break; + } + + return 0; +} + +//=============================================================================================================================== +//! Given a string containing time units, this sets up the appropriate multiplier and display units for the simulation +//=============================================================================================================================== +int CSimulation::nDoSimulationTimeMultiplier(string const* strIn) +{ + // First decide what the time units are + int const nTimeUnits = nDoTimeUnits(strIn); + + // Next set up the correct multiplier, since m_dTimeStep is in hours + switch (nTimeUnits) + { + case TIME_UNKNOWN: + return RTN_ERR_TIME_UNITS; + break; + + case TIME_HOURS: + m_dDurationUnitsMult = 1; // Multiplier for hours + m_strDurationUnits = "hours"; + break; + + case TIME_DAYS: + m_dDurationUnitsMult = 24; // Multiplier for days -> hours + m_strDurationUnits = "days"; + break; + + case TIME_MONTHS: + m_dDurationUnitsMult = 24 * 30.416667; // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years) + m_strDurationUnits = "months"; + break; + + case TIME_YEARS: + m_dDurationUnitsMult = 24 * 365.25; // Multiplier for years -> hours + m_strDurationUnits = "years"; + break; + } + + return RTN_OK; +} + +//=============================================================================================================================== +//! This finds time units in a string +//=============================================================================================================================== +int CSimulation::nDoTimeUnits(string const* strIn) +{ + if (strIn->find("hour") != string::npos) + return TIME_HOURS; + + else if (strIn->find("day") != string::npos) + return TIME_DAYS; + + else if (strIn->find("month") != string::npos) + return TIME_MONTHS; + + else if (strIn->find("year") != string::npos) + return TIME_YEARS; + + else + return TIME_UNKNOWN; +} + +//=============================================================================================================================== +//! Opens the log file +//=============================================================================================================================== +bool CSimulation::bOpenLogFile(void) +{ + if (m_nLogFileDetail == 0) + { + LogStream.open("/dev/null", ios::out | ios::trunc); + cout << "Warning: log file is not writting" << endl; + } + + else + LogStream.open(m_strLogFile.c_str(), ios::out | ios::trunc); + + if (!LogStream) + { + // Error, cannot open log file + cerr << ERR << "cannot open " << m_strLogFile << " for output" << endl; + return false; + } + + return true; +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the DEM file +//=============================================================================================================================== +void CSimulation::AnnounceReadBasementDEM(void) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_BASEMENT << pstrChangeToForwardSlash(&m_strInitialBasementDEMFile) << endl; +#else + cout << READING_BASEMENT << m_strInitialBasementDEMFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now allocating memory +//=============================================================================================================================== +void CSimulation::AnnounceAllocateMemory(void) +{ + cout << ALLOCATE_MEMORY << endl; +} + +//=============================================================================================================================== +//! Tells the user that we are now adding layers +//=============================================================================================================================== +void CSimulation::AnnounceAddLayers(void) +{ + // Tell the user what is happening + cout << ADD_LAYERS << endl; +} + +//=============================================================================================================================== +//! Now reading raster GIS files +//=============================================================================================================================== +void CSimulation::AnnounceReadRasterFiles(void) +{ + cout << READING_RASTER_FILES << endl; +} + +//=============================================================================================================================== +//! Now reading vector GIS files +//=============================================================================================================================== +void CSimulation::AnnounceReadVectorFiles(void) +{ + cout << READING_VECTOR_FILES << endl; +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the Landscape category GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadLGIS(void) const +{ + // Tell the user what is happening + if (! m_strInitialLandformFile.empty()) +#ifdef _WIN32 + cout << READING_LANDFORM_FILE << pstrChangeToForwardSlash(&m_strInitialLandformFile) << endl; + +#else + cout << READING_LANDFORM_FILE << m_strInitialLandformFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the Intervention class GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadICGIS(void) const +{ + // Tell the user what is happening + if (! m_strInterventionClassFile.empty()) +#ifdef _WIN32 + cout << READING_INTERVENTION_CLASS_FILE << pstrChangeToForwardSlash(&m_strInterventionClassFile) << endl; + +#else + cout << READING_INTERVENTION_CLASS_FILE << m_strInterventionClassFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the Intervention height GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadIHGIS(void) const +{ + // Tell the user what is happening + if (! m_strInterventionHeightFile.empty()) +#ifdef _WIN32 + cout << READING_INTERVENTION_HEIGHT_FILE << pstrChangeToForwardSlash(&m_strInterventionHeightFile) << endl; + +#else + cout << READING_INTERVENTION_HEIGHT_FILE << m_strInterventionHeightFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the deep water wave values GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadDeepWaterWaveValuesGIS(void) const +{ + // Tell the user what is happening + if (! m_strDeepWaterWavesInputFile.empty()) +#ifdef _WIN32 + cout << READING_DEEP_WATER_WAVE_FILE << pstrChangeToForwardSlash(&m_strDeepWaterWavesInputFile) << endl; + +#else + cout << READING_DEEP_WATER_WAVE_FILE << m_strDeepWaterWavesInputFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the sediment input events GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadSedimentEventInputValuesGIS(void) const +{ + // Tell the user what is happening + if (! m_strSedimentInputEventFile.empty()) +#ifdef _WIN32 + cout << READING_SED_INPUT_EVENT_FILE << pstrChangeToForwardSlash(&m_strSedimentInputEventFile) << endl; + +#else + cout << READING_SED_INPUT_EVENT_FILE << m_strSedimentInputEventFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the flood location GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadFloodLocationGIS(void) const +{ + // Tell the user what is happening + if (! m_strFloodLocationShapefile.empty()) +#ifdef _WIN32 + cout << READING_FLOOD_LOCATION << pstrChangeToForwardSlash(&m_strFloodLocationShapefile) << endl; + +#else + cout << READING_FLOOD_LOCATION << m_strFloodLocationShapefile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial suspended sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialSuspSedGIS(void) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_SUSPENDED_SEDIMENT_FILE << pstrChangeToForwardSlash(&m_strInitialSuspSedimentFile) << endl; +#else + cout << READING_SUSPENDED_SEDIMENT_FILE << m_strInitialSuspSedimentFile << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial fine unconsolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialFineUnconsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_UNCONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialFineUnconsSedimentFile[nLayer]) << endl; +#else + cout << READING_UNCONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialFineUnconsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial sand unconsolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialSandUnconsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_UNCONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialSandUnconsSedimentFile[nLayer]) << endl; +#else + cout << READING_UNCONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialSandUnconsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial coarse unconsolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialCoarseUnconsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_UNCONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialCoarseUnconsSedimentFile[nLayer]) << endl; +#else + cout << READING_UNCONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialCoarseUnconsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial fine consolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialFineConsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_CONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialFineConsSedimentFile[nLayer]) << endl; +#else + cout << READING_CONS_FINE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialFineConsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial sand consolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialSandConsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_CONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialSandConsSedimentFile[nLayer]) << endl; +#else + cout << READING_CONS_SAND_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialSandConsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Tells the user that we are now reading the initial coarse consolidated sediment depth GIS file +//=============================================================================================================================== +void CSimulation::AnnounceReadInitialCoarseConsSedGIS(int const nLayer) const +{ + // Tell the user what is happening +#ifdef _WIN32 + cout << READING_CONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << pstrChangeToForwardSlash(&m_VstrInitialCoarseConsSedimentFile[nLayer]) << endl; +#else + cout << READING_CONS_COARSE_SEDIMENT_FILE << nLayer + 1 << "): " << m_VstrInitialCoarseConsSedimentFile[nLayer] << endl; +#endif +} + +//=============================================================================================================================== +//! Now reading tide data file +//=============================================================================================================================== +void CSimulation::AnnounceReadTideData(void) const +{ +#ifdef _WIN32 + cout << READING_TIDE_DATA_FILE << pstrChangeToForwardSlash(&m_strTideDataFile) << endl; +#else + cout << READING_TIDE_DATA_FILE << m_strTideDataFile << endl; +#endif +} + +//=============================================================================================================================== +//! Now reading the SCAPE shape function file +//=============================================================================================================================== +void CSimulation::AnnounceReadSCAPEShapeFunctionFile(void) +{ + cout << READING_SCAPE_SHAPE_FUNCTION_FILE << endl; +} + +//=============================================================================================================================== +//! Tells the user that we are now initializing +//=============================================================================================================================== +void CSimulation::AnnounceFinalInitialization(void) +{ + // Tell the user what is happening + cout << INITIALIZING_FINAL << endl; +} + +//=============================================================================================================================== +//! Tell the user that the simulation is now running +//=============================================================================================================================== +void CSimulation::AnnounceIsRunning(void) +{ + cout << RUN_NOTICE << endl; +} + +//=============================================================================================================================== +//! Return a space-separated string containing the names of the raster GIS output files +//=============================================================================================================================== +string CSimulation::strListRasterFiles(void) const +{ + string strTmp; + + if (m_bBasementElevSave) + { + strTmp.append(RASTER_BASEMENT_ELEVATION_CODE); + strTmp.append(", "); + } + + if (m_bSedIncTalusTopSurfSave) + { + strTmp.append(RASTER_SEDIMENT_TOP_CODE); + strTmp.append(", "); + } + + if (m_bTopSurfIncSeaSave) + { + strTmp.append(RASTER_TOP_ELEVATION_INC_SEA_CODE); + strTmp.append(", "); + } + + if (m_bTalusSave) + { + strTmp.append(RASTER_TALUS_CODE); + strTmp.append(", "); + } + + if (m_bSeaDepthSave) + { + strTmp.append(RASTER_SEA_DEPTH_CODE); + strTmp.append(", "); + } + + if (m_bAvgSeaDepthSave) + { + strTmp.append(RASTER_AVG_SEA_DEPTH_CODE); + strTmp.append(", "); + } + + if (m_bSeaMaskSave) + { + strTmp.append(RASTER_INUNDATION_MASK_CODE); + strTmp.append(", "); + } + + if (m_bWaveHeightSave) + { + strTmp.append(RASTER_WAVE_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bWaveAngleSave) + { + strTmp.append(RASTER_WAVE_ORIENTATION_CODE); + strTmp.append(", "); + } + + if (m_bAvgWaveHeightSave) + { + strTmp.append(RASTER_AVG_WAVE_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bBeachProtectionSave) + { + strTmp.append(RASTER_BEACH_PROTECTION_CODE); + strTmp.append(", "); + } + + if (m_bPotentialPlatformErosionSave) + { + strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bPotentialPlatformErosionMaskSave) + { + strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE); + strTmp.append(", "); + } + + if (m_bBeachDepositionSave) + { + strTmp.append(RASTER_BEACH_DEPOSITION_CODE); + strTmp.append(", "); + } + + if (m_bTotalBeachDepositionSave) + { + strTmp.append(RASTER_TOTAL_BEACH_DEPOSITION_CODE); + strTmp.append(", "); + } + + if (m_bBeachMaskSave) + { + strTmp.append(RASTER_BEACH_MASK_CODE); + strTmp.append(", "); + } + + if (m_bActualPlatformErosionSave) + { + strTmp.append(RASTER_ACTUAL_PLATFORM_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bTotalPotentialPlatformErosionSave) + { + strTmp.append(RASTER_TOTAL_POTENTIAL_PLATFORM_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bTotalActualPlatformErosionSave) + { + strTmp.append(RASTER_TOTAL_ACTUAL_PLATFORM_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bPotentialBeachErosionSave) + { + strTmp.append(RASTER_POTENTIAL_BEACH_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bActualBeachErosionSave) + { + strTmp.append(RASTER_ACTUAL_BEACH_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bTotalPotentialBeachErosionSave) + { + strTmp.append(RASTER_TOTAL_POTENTIAL_BEACH_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bTotalActualBeachErosionSave) + { + strTmp.append(RASTER_TOTAL_ACTUAL_BEACH_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bLandformSave) + { + strTmp.append(RASTER_LANDFORM_CODE); + strTmp.append(", "); + } + + if (m_bSlopeConsSedSave) + { + strTmp.append(RASTER_SLOPE_OF_CONSOLIDATED_SEDIMENT_CODE); + strTmp.append(", "); + } + + if (m_bInterventionClassSave) + { + strTmp.append(RASTER_INTERVENTION_CLASS_CODE); + strTmp.append(", "); + } + + if (m_bInterventionHeightSave) + { + strTmp.append(RASTER_INTERVENTION_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bHaveFineSediment && m_bSuspSedSave) + { + strTmp.append(RASTER_SUSP_SED_CODE); + strTmp.append(", "); + } + + if (m_bHaveFineSediment && m_bAvgSuspSedSave) + { + strTmp.append(RASTER_AVG_SUSP_SED_CODE); + strTmp.append(", "); + } + + if (m_bHaveFineSediment && m_bFineUnconsSedSave) + { + strTmp.append(RASTER_FINE_UNCONS_CODE); + strTmp.append(", "); + } + + if (m_bHaveSandSediment && m_bSandUnconsSedSave) + { + strTmp.append(RASTER_SAND_UNCONS_CODE); + strTmp.append(", "); + } + + if (m_bHaveCoarseSediment && m_bCoarseUnconsSedSave) + { + strTmp.append(RASTER_COARSE_UNCONS_CODE); + strTmp.append(", "); + } + + if (m_bHaveFineSediment && m_bFineConsSedSave) + { + strTmp.append(RASTER_FINE_CONS_CODE); + strTmp.append(", "); + } + + if (m_bHaveSandSediment && m_bSandConsSedSave) + { + strTmp.append(RASTER_SAND_CONS_CODE); + strTmp.append(", "); + } + + if (m_bHaveCoarseSediment && m_bCoarseConsSedSave) + { + strTmp.append(RASTER_COARSE_CONS_CODE); + strTmp.append(", "); + } + + if (m_bRasterCoastlineSave) + { + strTmp.append(RASTER_COAST_CODE); + strTmp.append(", "); + } + + if (m_bRasterNormalProfileSave) + { + strTmp.append(RASTER_COAST_NORMAL_CODE); + strTmp.append(", "); + } + + if (m_bActiveZoneSave) + { + strTmp.append(RASTER_ACTIVE_ZONE_CODE); + strTmp.append(", "); + } + + if (m_bRasterPolygonSave) + { + strTmp.append(RASTER_POLYGON_CODE); + strTmp.append(", "); + } + + if (m_bPotentialPlatformErosionMaskSave) + { + strTmp.append(RASTER_POTENTIAL_PLATFORM_EROSION_MASK_CODE); + strTmp.append(", "); + } + + if (m_bSedimentInputEventSave) + { + strTmp.append(RASTER_SEDIMENT_INPUT_EVENT_CODE); + strTmp.append(", "); + } + + // Remove the trailing comma and space + if (strTmp.size() > 2) + strTmp.resize(strTmp.size() - 2); + + return strTmp; +} + +//=============================================================================================================================== +//! Return a space-separated string containing the names of the vector GIS output files +//=============================================================================================================================== +string CSimulation::strListVectorFiles(void) const +{ + string strTmp; + + if (m_bCoastSave) + { + strTmp.append(VECTOR_COAST_CODE); + strTmp.append(", "); + } + + if (m_bNormalsSave) + { + strTmp.append(VECTOR_NORMALS_CODE); + strTmp.append(", "); + } + + if (m_bInvalidNormalsSave) + { + strTmp.append(VECTOR_INVALID_NORMALS_CODE); + strTmp.append(", "); + } + + if (m_bWaveAngleAndHeightSave) + { + strTmp.append(VECTOR_WAVE_ANGLE_AND_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bAvgWaveAngleAndHeightSave) + { + strTmp.append(VECTOR_AVG_WAVE_ANGLE_AND_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bCoastCurvatureSave) + { + strTmp.append(VECTOR_COAST_CURVATURE_CODE); + strTmp.append(", "); + } + + if (m_bWaveEnergySinceCollapseSave) + { + strTmp.append(VECTOR_WAVE_ENERGY_SINCE_COLLAPSE_CODE); + strTmp.append(", "); + } + + if (m_bMeanWaveEnergySave) + { + strTmp.append(VECTOR_MEAN_WAVE_ENERGY_CODE); + strTmp.append(", "); + } + + if (m_bBreakingWaveHeightSave) + { + strTmp.append(VECTOR_BREAKING_WAVE_HEIGHT_CODE); + strTmp.append(", "); + } + + if (m_bPolygonNodeSave) + { + strTmp.append(VECTOR_POLYGON_NODE_CODE); + strTmp.append(", "); + } + + if (m_bPolygonBoundarySave) + { + strTmp.append(VECTOR_POLYGON_BOUNDARY_CODE); + strTmp.append(", "); + } + + if (m_bCliffNotchSave) + { + strTmp.append(VECTOR_CLIFF_NOTCH_ACTIVE_CODE); + strTmp.append(", "); + } + + if (m_bShadowBoundarySave) + { + strTmp.append(VECTOR_SHADOW_ZONE_BOUNDARY_CODE); + strTmp.append(", "); + } + + if (m_bShadowDowndriftBoundarySave) + { + strTmp.append(VECTOR_DOWNDRIFT_ZONE_BOUNDARY_CODE); + strTmp.append(", "); + } + + if (m_bDeepWaterWaveAngleAndHeightSave) + { + strTmp.append(VECTOR_DEEP_WATER_WAVE_ANGLE_AND_HEIGHT_CODE); + strTmp.append(", "); + } + + // Remove the trailing comma and space + if (strTmp.size() > 2) + strTmp.resize(strTmp.size() - 2); + + return strTmp; +} + +//=============================================================================================================================== +//! Return a space-separated string containing the names of the time series output files +//=============================================================================================================================== +string CSimulation::strListTSFiles(void) const +{ + string strTmp; + + if (m_bSeaAreaTSSave) + { + strTmp.append(TIME_SERIES_SEA_AREA_CODE); + strTmp.append(", "); + } + + if (m_bSWLTSSave) + { + strTmp.append(TIME_SERIES_SWL_CODE); + strTmp.append(", "); + } + + if (m_bActualPlatformErosionTSSave) + { + strTmp.append(TIME_SERIES_PLATFORM_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bCliffCollapseErosionTSSave) + { + strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bCliffCollapseDepositionTSSave) + { + strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_CODE); + strTmp.append(", "); + } + + if (m_bCliffCollapseNetTSSave) + { + strTmp.append(TIME_SERIES_CLIFF_COLLAPSE_NET_CODE); + strTmp.append(", "); + } + + if (m_bBeachErosionTSSave) + { + strTmp.append(TIME_SERIES_BEACH_EROSION_CODE); + strTmp.append(", "); + } + + if (m_bBeachDepositionTSSave) + { + strTmp.append(TIME_SERIES_BEACH_DEPOSITION_CODE); + strTmp.append(", "); + } + + if (m_bBeachSedimentChangeNetTSSave) + { + strTmp.append(TIME_SERIES_BEACH_CHANGE_NET_CODE); + strTmp.append(", "); + } + + if (m_bSuspSedTSSave) + { + strTmp.append(TIME_SERIES_SUSPENDED_SEDIMENT_CODE); + strTmp.append(", "); + } + + if (m_bFloodSetupSurgeTSSave) + { + strTmp.append(TIME_SERIES_FLOOD_SETUP_SURGE_CODE); + strTmp.append(", "); + } + + if (m_bFloodSetupSurgeRunupTSSave) + { + strTmp.append(TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE); + strTmp.append(", "); + } + + if (m_bCliffNotchElevTSSave) + { + strTmp.append(TIME_SERIES_CLIFF_NOTCH_ELEV_CODE); + strTmp.append(", "); + } + + // Remove the trailing comma and space + if (strTmp.size() > 2) + strTmp.resize(strTmp.size() - 2); + + return strTmp; +} + +//=============================================================================================================================== +//! This member function intialises the time series files +//=============================================================================================================================== +bool CSimulation::bSetUpTSFiles(void) +{ + string strTSFile; + + if (m_bSeaAreaTSSave) + { + // Start with wetted area + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_SEA_AREA_NAME); + strTSFile.append(CSVEXT); + + // Open sea area time-series CSV file + SeaAreaTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! SeaAreaTSStream) + { + // Error, cannot open wetted area time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bSWLTSSave) + { + // Now SWL + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_SWL_NAME); + strTSFile.append(CSVEXT); + + // Open SWL time-series CSV file + SWLTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! SWLTSStream) + { + // Error, cannot open SWL time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bActualPlatformErosionTSSave) + { + // Erosion (fine, sand, coarse) + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_PLATFORM_EROSION_NAME); + strTSFile.append(CSVEXT); + + // Open erosion time-series CSV file + PlatformErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! PlatformErosionTSStream) + { + // Error, cannot open erosion time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bCliffCollapseErosionTSSave) + { + // Erosion due to cliff collapse + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_EROSION_NAME); + strTSFile.append(CSVEXT); + + // Open cliff collapse erosion time-series CSV file + CliffCollapseErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! CliffCollapseErosionTSStream) + { + // Error, cannot open cliff collapse erosion time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bCliffCollapseDepositionTSSave) + { + // Deposition due to cliff collapse + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_DEPOSITION_NAME); + strTSFile.append(CSVEXT); + + // Open cliff collapse deposition time-series CSV file + CliffCollapseDepositionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! CliffCollapseDepositionTSStream) + { + // Error, cannot open cliff collapse deposition time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bCliffCollapseNetTSSave) + { + // Net change in unconsolidated sediment due to cliff collapse + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_CLIFF_COLLAPSE_NET_NAME); + strTSFile.append(CSVEXT); + + // Open net cliff collapse time-series CSV file + CliffCollapseNetChangeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! CliffCollapseNetChangeTSStream) + { + // Error, cannot open net cliff collapse time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bBeachErosionTSSave) + { + // Beach erosion + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_BEACH_EROSION_NAME); + strTSFile.append(CSVEXT); + + // Open beach erosion time-series CSV file + BeachErosionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! BeachErosionTSStream) + { + // Error, cannot open beach erosion time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bBeachDepositionTSSave) + { + // Beach deposition + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_BEACH_DEPOSITION_NAME); + strTSFile.append(CSVEXT); + + // Open beach deposition time-series CSV file + BeachDepositionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! BeachDepositionTSStream) + { + // Error, cannot open beach deposition time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bBeachSedimentChangeNetTSSave) + { + // Beach sediment change + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_BEACH_CHANGE_NET_NAME); + strTSFile.append(CSVEXT); + + // Open net beach sediment change time-series CSV file + BeachSedimentNetChangeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! BeachSedimentNetChangeTSStream) + { + // Error, cannot open beach sediment change time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bSuspSedTSSave) + { + // Sediment load + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_SUSPENDED_SEDIMENT_NAME); + strTSFile.append(CSVEXT); + + // Open sediment load time-series CSV file + FineSedSuspensionTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! FineSedSuspensionTSStream) + { + // Error, cannot open sediment load time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bFloodSetupSurgeTSSave) + { + // Sediment load + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_FLOOD_SETUP_SURGE_CODE); + strTSFile.append(CSVEXT); + + // Open sediment load time-series CSV file + FloodSetupSurgeTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! FloodSetupSurgeTSStream) + { + // Error, cannot open sediment load time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bFloodSetupSurgeRunupTSSave) + { + // Sediment load + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_FLOOD_SETUP_SURGE_RUNUP_CODE); + strTSFile.append(CSVEXT); + + // Open sediment load time-series CSV file + FloodSetupSurgeRunupTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! FloodSetupSurgeRunupTSStream) + { + // Error, cannot open sediment load time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + if (m_bCliffNotchElevTSSave) + { + // Elevation of cliff notch + strTSFile = m_strOutPath; + strTSFile.append(TIME_SERIES_CLIFF_NOTCH_ELEV_NAME); + strTSFile.append(CSVEXT); + + // Open cliff notch elevation time-series CSV file + CliffNotchElevTSStream.open(strTSFile.c_str(), ios::out | ios::trunc); + if (! CliffNotchElevTSStream) + { + // Error, cannot open cliff notch elevation time-series file + cerr << ERR << "cannot open " << strTSFile << " for output" << endl; + return false; + } + } + + return true; +} + +//=============================================================================================================================== +//! Checks to see if the simulation has gone on too long, amongst other things +//=============================================================================================================================== +bool CSimulation::bTimeToQuit(void) +{ + // Add timestep to the total time simulated so far + m_dSimElapsed += m_dTimeStep; + + if (m_dSimElapsed >= m_dSimDuration) + { + // It is time to quit + m_dSimElapsed = m_dSimDuration; + AnnounceProgress(); + return true; + } + + // Not quitting, so increment the timestep count, and recalc total timesteps + m_ulIter++; + m_ulTotTimestep = static_cast(dRound(m_dSimDuration / m_dTimeStep)); + + // Check to see if we have done CLOCK_CHECK_ITERATION timesteps: if so, it is time to reset the CPU time running total in case the clock() function later rolls over + if (0 == m_ulIter % CLOCK_CHECK_ITERATION) + DoCPUClockReset(); + + // Not yet time to quit + return false; +} + +//=============================================================================================================================== +//! Returns a string, hopefully giving the name of the computer on which the simulation is running +//=============================================================================================================================== +string CSimulation::strGetComputerName(void) +{ + string strComputerName; + +#ifdef _WIN32 + // Being compiled to run under Windows, either by MS VC++, Borland C++, or Cygwin + strComputerName = getenv("COMPUTERNAME"); +#else + // Being compiled for another platform; assume for Linux-Unix + char szHostName[BUF_SIZE] = ""; + gethostname(szHostName, BUF_SIZE); + + strComputerName = szHostName; + + if (strComputerName.empty()) + strComputerName = "Unknown Computer"; + +#endif + + return strComputerName; +} + +//=============================================================================================================================== +//! Resets the CPU clock timer to prevent it 'rolling over', as can happen during long runs. This is a particularly problem under Unix systems where the value returned by clock() is defined in microseconds (for compatibility with systems that have CPU clocks with much higher resolution) i.e. CLOCKS_PER_SEC is 1000000 rather than the more usual 1000. In this case, the value returned from clock() will wrap around after accumulating only 2147 seconds of CPU time (about 36 minutes). +//=============================================================================================================================== +void CSimulation::DoCPUClockReset(void) +{ + if (static_cast(-1) == clock()) + { + // Error + LogStream << "CPU time not available" << endl; + m_dCPUClock = -1; + return; + } + + // OK, so carry on + double dClkThis = static_cast(clock()); + dClkThis -= CLOCK_T_MIN; // necessary when clock_t is signed, to make dClkThis unsigned + + if (dClkThis < m_dClkLast) + { + // Clock has 'rolled over' + m_dCPUClock += (CLOCK_T_RANGE + 1 - m_dClkLast); // this elapsed before rollover + m_dCPUClock += dClkThis; // this elapsed after rollover + +#ifdef CLOCKCHECK + // For debug purposes + LogStream << "Rolled over: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << endl + << "\t" + << " before rollover=" << (CLOCK_T_RANGE + 1 - m_dClkLast) << endl + << "\t" + << " after rollover=" << dClkThis << endl + << "\t" + << " ADDED=" << (CLOCK_T_RANGE + 1 - m_dClkLast + dClkThis) << endl; +#endif + } + + else + { + // No rollover + m_dCPUClock += (dClkThis - m_dClkLast); + +#ifdef CLOCKCHECK + // For debug purposes + LogStream << "No rollover: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << " ADDED=" << dClkThis - m_dClkLast << endl; +#endif + } + + // Reset for next time + m_dClkLast = dClkThis; +} + +//=============================================================================================================================== +//! Announce the end of the simulation +//=============================================================================================================================== +void CSimulation::AnnounceSimEnd(void) +{ + cout << endl << FINAL_OUTPUT << endl; +} + +// //=============================================================================================================================== +// //! Calculates and displays time elapsed in terms of CPU time and real time, also calculates time per timestep in terms of both CPU time and real time +// //=============================================================================================================================== +// void CSimulation::CalcTime(double const dRunLength) +// { +// // Reset CPU count for last time +// DoCPUClockReset(); +// +// if (! bFPIsEqual(m_dCPUClock, -1.0, TOLERANCE)) +// { +// // Calculate CPU time in secs +// double const dDuration = m_dCPUClock / CLOCKS_PER_SEC; +// +// // And write CPU time out to OutStream and LogStream +// OutStream << "CPU time elapsed: " << strDispTime(dDuration, false, true); +// LogStream << "CPU time elapsed: " << strDispTime(dDuration, false, true); +// +// // Calculate CPU time per timestep +// double const dPerTimestep = dDuration / static_cast(m_ulTotTimestep); +// +// // And write CPU time per timestep to OutStream and LogStream +// OutStream << fixed << setprecision(4) << " (" << dPerTimestep << " per timestep)" << endl; +// LogStream << fixed << setprecision(4) << " (" << dPerTimestep << " per timestep)" << endl; +// +// // Calculate ratio of CPU time to time simulated +// OutStream << resetiosflags(ios::floatfield); +// OutStream << fixed << setprecision(0) << "In terms of CPU time, this is "; +// LogStream << resetiosflags(ios::floatfield); +// LogStream << fixed << setprecision(0) << "In terms of CPU time, this is "; +// +// if (dDuration > dRunLength) +// { +// OutStream << dDuration / dRunLength << " x slower than reality" << endl; +// LogStream << dDuration / dRunLength << " x slower than reality" << endl; +// } +// +// else +// { +// OutStream << dRunLength / dDuration << " x faster than reality" << endl; +// LogStream << dRunLength / dDuration << " x faster than reality" << endl; +// } +// } +// +// // Calculate run time +// double const dDuration = difftime(m_tSysEndTime, m_tSysStartTime); +// +// // And write run time out to OutStream and LogStream +// OutStream << "Run time elapsed: " << strDispTime(dDuration, false, false); +// LogStream << "Run time elapsed: " << strDispTime(dDuration, false, false); +// +// // Calculate run time per timestep +// double const dPerTimestep = dDuration / static_cast(m_ulTotTimestep); +// +// // And write run time per timestep to OutStream and LogStream +// OutStream << resetiosflags(ios::floatfield); +// OutStream << " (" << fixed << setprecision(4) << dPerTimestep << " per timestep)" << endl; +// LogStream << resetiosflags(ios::floatfield); +// LogStream << " (" << fixed << setprecision(4) << dPerTimestep << " per timestep)" << endl; +// +// // Calculate ratio of run time to time simulated +// OutStream << fixed << setprecision(0) << "In terms of run time, this is "; +// LogStream << fixed << setprecision(0) << "In terms of run time, this is "; +// +// if (dDuration > dRunLength) +// { +// OutStream << dDuration / dRunLength << " x slower than reality" << endl; +// LogStream << dDuration / dRunLength << " x slower than reality" << endl; +// } +// +// else +// { +// OutStream << dRunLength / dDuration << " x faster than reality" << endl; +// LogStream << dRunLength / dDuration << " x faster than reality" << endl; +// } +// } + +//=============================================================================================================================== +//! strDispSimTime returns a string formatted as year Julian_day hour, given a parameter in hours +//=============================================================================================================================== +string CSimulation::strDispSimTime(const double dTimeIn) +{ + // Make sure no negative times + double dTmpTime = tMax(dTimeIn, 0.0); + + string strTime; + + // Constants + double const dHoursInYear = 24 * 365; // it was 365.25 + double const dHoursInDay = 24; + + // Display years + if (dTmpTime >= dHoursInYear) + { + double const dYears = floor(dTmpTime / dHoursInYear); + dTmpTime -= (dYears * dHoursInYear); + + strTime = to_string(static_cast(dYears)); + strTime.append("y "); + } + else + strTime = "0y "; + + // Display Julian days + if (dTmpTime >= dHoursInDay) + { + double const dJDays = floor(dTmpTime / dHoursInDay); + dTmpTime -= (dJDays * dHoursInDay); + + stringstream ststrTmp; + ststrTmp << FillToWidth('0', 3) << static_cast(dJDays); + strTime.append(ststrTmp.str()); + strTime.append("d "); + } + else + strTime.append("000d "); + + // Display hours + stringstream ststrTmp; + ststrTmp << FillToWidth('0', 2) << static_cast(dTmpTime); + strTime.append(ststrTmp.str()); + strTime.append("h"); + + return strTime; +} + +//=============================================================================================================================== +//! strDispTime returns a string formatted as h:mm:ss, given a parameter in seconds, with rounding and fractions of a second if desired +//=============================================================================================================================== +string CSimulation::strDispTime(const double dTimeIn, const bool bRound, const bool bFrac) +{ + // Make sure no negative times + double dTime = tMax(dTimeIn, 0.0); + + string strTime; + + if (bRound) + dTime = dRound(dTime); + + unsigned long ulTimeIn = static_cast(floor(dTime)); + dTime -= static_cast(ulTimeIn); + + // Hours + if (ulTimeIn >= 3600) + { + // Display some hours + unsigned long const ulHours = ulTimeIn / 3600ul; + ulTimeIn -= (ulHours * 3600ul); + + strTime = to_string(ulHours); + strTime.append(":"); + } + else + strTime = "0:"; + + // Minutes + if (ulTimeIn >= 60) + { + // display some minutes + unsigned long const ulMins = ulTimeIn / 60ul; + ulTimeIn -= (ulMins * 60ul); + + stringstream ststrTmp; + ststrTmp << FillToWidth('0', 2) << ulMins; + strTime.append(ststrTmp.str()); + strTime.append(":"); + } + else + strTime.append("00:"); + + // Seconds + stringstream ststrTmp; + ststrTmp << FillToWidth('0', 2) << ulTimeIn; + strTime.append(ststrTmp.str()); + + if (bFrac) + { + // Fractions of a second + strTime.append("."); + ststrTmp.clear(); + ststrTmp.str(string()); + ststrTmp << FillToWidth('0', 2) << static_cast(dTime * 100); + strTime.append(ststrTmp.str()); + } + + return strTime; +} + +//=============================================================================================================================== +//! Returns the date and time on which the program was compiled +//=============================================================================================================================== +string CSimulation::strGetBuild(void) +{ + string strBuild("("); + strBuild.append(__TIME__); + strBuild.append(" "); + strBuild.append(__DATE__); +#ifdef _DEBUG + strBuild.append(" DEBUG"); +#endif + strBuild.append(" build)"); + + return strBuild; +} + +//=============================================================================================================================== +//! Displays information regarding the progress of the simulation +//=============================================================================================================================== +void CSimulation::AnnounceProgress(void) +{ + if (isatty(fileno(stdout))) + { + // Stdout is connected to a tty, so not running as a background job + static double sdElapsed = 0; + static double sdToGo = 0; + time_t const tNow = time(nullptr); + + // Calculate time elapsed and remaining + sdElapsed = difftime(tNow, m_tSysStartTime); + sdToGo = (sdElapsed * m_dSimDuration / m_dSimElapsed) - sdElapsed; + + // Tell the user about progress (note need to make several separate calls to cout here, or MS VC++ compiler appears to get confused) + cout << SIMULATING << strDispSimTime(m_dSimElapsed); + cout << fixed << setprecision(3) << setw(9) << 100 * m_dSimElapsed / m_dSimDuration; + cout << "% (elapsed " << strDispTime(sdElapsed, false, false) << " remaining "; + + cout << strDispTime(sdToGo, false, false) << ") "; + + // Add a 'marker' for GIS saves etc. + if (m_bSaveGISThisIter) + cout << setw(9) << "GIS" + to_string(m_nGISSave); + else if (m_bSedimentInputThisIter) + cout << setw(9) << "SED INPUT"; + else + cout << setw(9) << SPACE; + + cout.flush(); + } +} + +//=============================================================================================================================== +//! This calculates and displays process statistics +//=============================================================================================================================== +void CSimulation::CalcProcessStats(void) +{ + string const NA = "Not available"; + + OutStream << endl; + OutStream << "Process statistics" << endl; + OutStream << "------------------" << endl; + +#ifdef _WIN32 + // First, find out which version of Windows we are running under + OSVERSIONINFOEX osvi; + BOOL bOsVersionInfoEx; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); // fill this much memory with zeros + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*)&osvi))) + { + // OSVERSIONINFOEX didn't work so try OSVERSIONINFO instead + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if (!GetVersionEx((OSVERSIONINFO*)&osvi)) + { + // That didn't work either, too risky to proceed so give up + OutStream << NA << endl; + return; + } + } + + // OK, we have Windows version so display it + OutStream << "Running under \t: "; + + switch (osvi.dwPlatformId) + { + case VER_PLATFORM_WIN32_NT: + if (osvi.dwMajorVersion <= 4) + OutStream << "Windows NT "; + + else if (5 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) + OutStream << "Windows 2000 "; + + else if (5 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion) + OutStream << "Windows XP "; + + else if (6 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) + OutStream << "Windows Vista "; + + else if (6 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion) + OutStream << "Windows 7 "; + + else if (6 == osvi.dwMajorVersion && 2 == osvi.dwMinorVersion) + OutStream << "Windows 8 "; + + else if (6 == osvi.dwMajorVersion && 3 == osvi.dwMinorVersion) + OutStream << "Windows 8.1 "; + + else if (10 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) + OutStream << "Windows 10 "; + + else if (11 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) + OutStream << "Windows 11 "; + + else + OutStream << "unknown Windows version "; + + // Display version, service pack (if any), and build number + if (osvi.dwMajorVersion <= 4) + OutStream << "version " << osvi.dwMajorVersion << "." << osvi.dwMinorVersion << " " << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl; + + else + OutStream << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl; + + break; + + case VER_PLATFORM_WIN32_WINDOWS: + if (4 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion) + { + OutStream << "Windows 95"; + + if ('C' == osvi.szCSDVersion[1] || 'B' == osvi.szCSDVersion[1]) + OutStream << " OSR2"; + + OutStream << endl; + } + + else if (4 == osvi.dwMajorVersion && 10 == osvi.dwMinorVersion) + { + OutStream << "Windows 98"; + + if ('A' == osvi.szCSDVersion[1]) + OutStream << "SE"; + + OutStream << endl; + } + + else if (4 == osvi.dwMajorVersion && 90 == osvi.dwMinorVersion) + OutStream << "Windows Me" << endl; + + else + OutStream << "unknown 16-bit Windows version " << endl; + + break; + + case VER_PLATFORM_WIN32s: + OutStream << "Win32s" << endl; + break; + } + + // Now get process timimgs: this only works under 32-bit windows + if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId) + { + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + if (GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser)) + { + ULARGE_INTEGER ul; + ul.LowPart = ftUser.dwLowDateTime; + ul.HighPart = ftUser.dwHighDateTime; + OutStream << "Time spent executing user code \t: " << strDispTime(static_cast(ul.QuadPart) * 1e-7, false) << endl; + ul.LowPart = ftKernel.dwLowDateTime; + ul.HighPart = ftKernel.dwHighDateTime; + OutStream << "Time spent executing kernel code \t: " << strDispTime(static_cast(ul.QuadPart) * 1e-7, false) << endl; + } + } + + else + OutStream << "Process timings \t: " << NA << endl; + + // Finally get more process statistics: this needs psapi.dll, so only proceed if it is present on this system + HINSTANCE hDLL = LoadLibrary("psapi.dll"); + + if (hDLL != NULL) + { + // The dll has been found + typedef BOOL(__stdcall * DLLPROC)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD); + DLLPROC ProcAdd; + + // Try to get the address of the function we will call + ProcAdd = (DLLPROC)GetProcAddress(hDLL, "GetProcessMemoryInfo"); + + if (ProcAdd) + { + // Address was found + PROCESS_MEMORY_COUNTERS pmc; + + // Now call the function + if ((ProcAdd)(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + OutStream << "Peak working set size \t: " << pmc.PeakWorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Current working set size \t: " << pmc.WorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Peak paged pool usage \t: " << pmc.QuotaPeakPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Current paged pool usage \t: " << pmc.QuotaPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Peak non-paged pool usage \t: " << pmc.QuotaPeakNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Current non-paged pool usage \t: " << pmc.QuotaNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Peak pagefile usage \t: " << pmc.PeakPagefileUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "Current pagefile usage \t: " << pmc.PagefileUsage / (1024.0 * 1024.0) << " Mb" << endl; + OutStream << "No. of page faults \t: " << pmc.PageFaultCount << endl; + } + } + + // Free the memory used by the dll + FreeLibrary(hDLL); + } + +#elif defined __GNUG__ + rusage ru; + + if (getrusage(RUSAGE_SELF, &ru) >= 0) + { + OutStream << "Time spent executing user code \t: " << strDispTime(static_cast(ru.ru_utime.tv_sec), false, true) << endl; + // OutStream << "ru_utime.tv_usec \t: " << ru.ru_utime.tv_usec << endl; + OutStream << "Time spent executing kernel code \t: " << strDispTime(static_cast(ru.ru_stime.tv_sec), false, true) << endl; + // OutStream << "ru_stime.tv_usec \t: " << ru.ru_stime.tv_usec << endl; + // OutStream << "Maximum resident set size \t: " << ru.ru_maxrss/1024.0 << " Mb" << endl; + // OutStream << "ixrss (???) \t: " << ru.ru_ixrss << endl; + // OutStream << "Sum of rm_asrss (???) \t: " << ru.ru_idrss << endl; + // OutStream << "isrss (???) \t: " << ru.ru_isrss << endl; + OutStream << "No. of page faults not requiring physical I/O\t: " << ru.ru_minflt << endl; + OutStream << "No. of page faults requiring physical I/O \t: " << ru.ru_majflt << endl; + // OutStream << "No. of times swapped out of main memory \t: " << ru.ru_nswap << endl; + // OutStream << "No. of times performed input (read request) \t: " << ru.ru_inblock << endl; + // OutStream << "No. of times performed output (write request)\t: " << ru.ru_oublock << endl; + // OutStream << "No. of signals received \t: " << ru.ru_nsignals << endl; + OutStream << "No. of voluntary context switches \t: " << ru.ru_nvcsw << endl; + OutStream << "No. of involuntary context switches \t: " << ru.ru_nivcsw << endl; + } + + else + OutStream << NA << endl; + +#else + OutStream << NA << endl; +#endif + + OutStream << endl; + +#ifdef _OPENMP +#pragma omp parallel + { + if (0 == omp_get_thread_num()) + { + OutStream << "Number of OpenMP threads \t: " << omp_get_num_threads() << endl; + OutStream << "Number of OpenMP processors \t: " << omp_get_num_procs() << endl; + + LogStream << "Number of OpenMP threads \t: " << omp_get_num_threads() << endl; + LogStream << "Number of OpenMP processors \t: " << omp_get_num_procs() << endl; + } + } +#endif + + time_t const tRunTime = m_tSysEndTime - m_tSysStartTime; + struct tm* ptmRunTime = gmtime(&tRunTime); + + OutStream << "Time required for simulation \t: " << put_time(ptmRunTime, "%T") << endl; + LogStream << "Time required for simulation \t: " << put_time(ptmRunTime, "%T") << endl; + + double const dSpeedUp = m_dSimDuration * 3600 / static_cast(tRunTime); + OutStream << setprecision(0); + OutStream << "Time simulated / time required for simulation\t: " << dSpeedUp << " x faster than reality" << endl; + + LogStream << setprecision(0); + LogStream << "Time simulated / time required for simulation\t: " << dSpeedUp << " x faster than reality" << endl; +} + +//=============================================================================================================================== +//! Returns an error message given an error code +//=============================================================================================================================== +string CSimulation::strGetErrorText(int const nErr) +{ + string strErr; + + switch (nErr) + { + case RTN_USER_ABORT: + strErr = "run ended by user"; + break; + + case RTN_ERR_BADPARAM: + strErr = "error in command-line parameter"; + break; + + case RTN_ERR_INI: + strErr = "error reading initialisation file"; + break; + + case RTN_ERR_CMEDIR: + strErr = "error in directory name"; + break; + + case RTN_ERR_RUNDATA: + strErr = "error reading run details file"; + break; + + case RTN_ERR_SCAPE_SHAPE_FUNCTION_FILE: + strErr = "error reading SCAPE shape function file"; + break; + + case RTN_ERR_TIDEDATAFILE: + strErr = "error reading tide data file"; + break; + + case RTN_ERR_LOGFILE: + strErr = "error creating log file"; + break; + + case RTN_ERR_OUTFILE: + strErr = "error creating text output file"; + break; + + case RTN_ERR_TSFILE: + strErr = "error creating time series file"; + break; + + case RTN_ERR_DEMFILE: + strErr = "error reading initial DEM file"; + break; + + case RTN_ERR_RASTER_FILE_READ: + strErr = "error reading raster GIS file"; + break; + + case RTN_ERR_VECTOR_FILE_READ: + strErr = "error reading vector GIS file"; + break; + + case RTN_ERR_MEMALLOC: + strErr = "error allocating memory"; + break; + + case RTN_ERR_RASTER_GIS_OUT_FORMAT: + strErr = "problem with raster GIS output format"; + break; + + case RTN_ERR_VECTOR_GIS_OUT_FORMAT: + strErr = "problem with vector GIS output format"; + break; + + case RTN_ERR_TEXT_FILE_WRITE: + strErr = "error writing text output file"; + break; + + case RTN_ERR_RASTER_FILE_WRITE: + strErr = "error writing raster GIS output file"; + break; + + case RTN_ERR_VECTOR_FILE_WRITE: + strErr = "error writing vector GIS output file"; + break; + + case RTN_ERR_TIMESERIES_FILE_WRITE: + strErr = "error writing time series output file"; + break; + + case RTN_ERR_LINETOGRID: + strErr = "error putting linear feature onto raster grid"; + break; + + case RTN_ERR_NOSEACELLS: + strErr = "no sea cells found"; + break; + + case RTN_ERR_GRID_TO_LINE: + strErr = "error when searching grid for linear feature"; + break; + + case RTN_ERR_NO_COAST: + strErr = "no coastlines found. Is the SWL correct?"; + break; + + case RTN_ERR_PROFILE_WRITE: + strErr = "error writing coastline-normal profiles"; + break; + + case RTN_ERR_TIME_UNITS: + strErr = "error in time units"; + break; + + case RTN_ERR_NO_SOLUTION_FOR_ENDPOINT: + strErr = "no solution when finding end point for coastline-normal line"; + break; + + case RTN_ERR_PROFILE_ENDPOINT_IS_INLAND: + strErr = "end point for coastline-normal line is not in the contiguous sea"; + break; + + case RTN_ERR_CLIFF_NOTCH: + strErr = "cliff notch is above sediment top elevation"; + break; + + case RTN_ERR_CLIFF_CANNOT_DEPOSIT_ALL: + strErr = "unable to deposit enough unconsolidated sediment (talus) from cliff collapse"; + break; + + case RTN_ERR_PROFILE_SPACING: + strErr = "coastline-normal profiles are too closely spaced"; + break; + + case RTN_ERR_NO_PROFILES_1: + strErr = "no coastline-normal profiles created, check the SWL"; + break; + + case RTN_ERR_NO_PROFILES_2: + strErr = "no coastline-normal profiles created during rasterization"; + break; + + case RTN_ERR_EDGE_OF_GRID: + strErr = "hit grid edge when eroding beach"; + break; + + case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_BEACH_EROSION: + strErr = "could not locate seaward end of profile when creating Dean profile for beach erosion"; + break; + + case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_UPCOAST_BEACH_DEPOSITION: + strErr = "could not locate seaward end of profile when creating Dean profile for up-coast beach deposition"; + break; + + case RTN_ERR_NO_SEAWARD_END_OF_PROFILE_DOWNCOAST_BEACH_DEPOSITION: + strErr = "could not locate seaward end of profile when creating Dean profile for down-coast beach deposition"; + break; + + case RTN_ERR_LANDFORM_TO_GRID: + strErr = "updating grid with landforms"; + break; + + case RTN_ERR_NO_TOP_LAYER: + strErr = "no top layer of sediment"; + break; + + case RTN_ERR_NO_ADJACENT_POLYGON: + strErr = "problem with polygon-to-polygon sediment routing sequence"; + break; + + case RTN_ERR_BAD_MULTILINE: + strErr = "inconsistent multiline"; + break; + + case RTN_ERR_CANNOT_INSERT_POINT: + strErr = "cannot insert point into multiline"; + break; + + case RTN_ERR_CANNOT_ASSIGN_COASTAL_LANDFORM: + strErr = "cannot assign coastal landform"; + break; + + case RTN_ERR_SHADOW_ZONE_FLOOD_FILL_NOGRID: + strErr = "start point for cell-by-cell fill of wave shadow zone is outside grid"; + break; + + case RTN_ERR_SHADOW_ZONE_FLOOD_START_POINT: + strErr = "could not find start point for cell-by-cell fill of wave shadow zone"; + break; + + case RTN_ERR_CSHORE_EMPTY_PROFILE: + strErr = "empty profile during during CShore wave propagation"; + break; + + case RTN_ERR_CSHORE_FILE_INPUT: + strErr = "creating file for CShore input"; + break; + + case RTN_ERR_READING_CSHORE_FILE_OUTPUT: + strErr = "reading CShore output file"; + break; + + case RTN_ERR_WAVE_INTERPOLATION_LOOKUP: + strErr = "during wave interpolation lookup"; + break; + + case RTN_ERR_GRIDCREATE: + strErr = "while running GDALGridCreate()"; + break; + + case RTN_ERR_COAST_CANT_FIND_EDGE_CELL: + strErr = "cannot find edge cell while constructing grid-edge profile"; + break; + + case RTN_ERR_CSHORE_ERROR: + strErr = "CShore did not finish correctly"; + break; + + case RTN_ERR_NO_CELL_UNDER_COASTLINE: + strErr = "Could not find cell under coastline"; + break; + + case RTN_ERR_OPEN_DEEP_WATER_WAVE_DATA: + strErr = "opening deep sea wave time series file"; + break; + + case RTN_ERR_READING_DEEP_WATER_WAVE_DATA: + strErr = "reading deep sea wave time series file"; + break; + + case RTN_ERR_BOUNDING_BOX: + strErr = "finding edges of the bounding box"; + break; + + case RTN_ERR_READING_SEDIMENT_INPUT_EVENT: + strErr = "reading sediment input event time series file"; + break; + + case RTN_ERR_SEDIMENT_INPUT_EVENT: + strErr = "simulating sediment input event"; + break; + + case RTN_ERR_SEDIMENT_INPUT_EVENT_LOCATION: + strErr = "location of sediment input event is outside grod"; + break; + + case RTN_ERR_WAVESTATION_LOCATION: + strErr = "location of wavestation is outside grid"; + break; + + case RTN_ERR_CLIFF_NOT_IN_POLYGON: + strErr = "cliff not in polygon"; + break; + + case RTN_ERR_CELL_MARKED_PROFILE_COAST_BUT_NOT_PROFILE: + strErr = "Cell marked as profile coast but not as profile"; + break; + + case RTN_ERR_TRACING_FLOOD: + strErr = "error tracing flood line on grid"; + break; + + case RTN_ERR_NO_START_FINISH_POINTS_TRACING_COAST: + strErr = "error tracing coastline on grid, no coast start-finish points found"; + break; + + case RTN_ERR_NO_VALID_COAST: + strErr = "error tracing coastline on grid, no valid coast found"; + break; + + case RTN_ERR_REPEATING_WHEN_TRACING_COAST: + strErr = "error tracing coastline on grid, coast search just repeats"; + break; + + case RTN_ERR_ZERO_LENGTH_COAST: + strErr = "error tracing coastline on grid, zero-length coast found"; + break; + + case RTN_ERR_COAST_TOO_SMALL: + strErr = "error tracing coastline on grid, coast below minimum permitted length"; + break; + + case RTN_ERR_IGNORING_COAST: + strErr = "error tracing coastline on grid, coast ignored"; + break; + + case RTN_ERR_TOO_LONG_TRACING_COAST: + strErr = "error tracing coastline on grid, too many times round tracing loop"; + break; + + case RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE_DIFFERENT_COASTS: + strErr = "intersection cell not found in hit profile"; + break; + + case RTN_ERR_POINT_NOT_FOUND_IN_MULTILINE_DIFFERENT_COASTS: + strErr = "point not found when truncating multiline for different coasts"; + break; + + case RTN_ERR_CELL_NOT_FOUND_IN_HIT_PROFILE: + strErr = "cell not found in hit profile"; + break; + + case RTN_ERR_CELL_IN_POLY_BUT_NO_POLY_COAST: + strErr = "cell marked as in polygon, but does not have polygon's coast"; + break; + + case RTN_ERR_CLIFF_TALUS_TO_UNCONS: + strErr = "cannot find closest point to coast when moving talus to unconsolidated sediment"; + break; + + case RTN_ERR_UNKNOWN: + strErr = "unknown error"; + break; + + default: + // should never get here + strErr = " error"; + } + + return strErr; +} + +//=============================================================================================================================== +//! Notifies the user that the simulation has ended, asks for keypress if necessary, and if compiled under GNU can send an email +//=============================================================================================================================== +void CSimulation::DoSimulationEnd(int const nRtn) +{ + // If we don't know the time that the run ended (e.g. because it did not finish correctly), then get it now + if (m_tSysEndTime == 0) + m_tSysEndTime = time(nullptr); + + switch (nRtn) + { + case (RTN_OK): + // normal ending + cout << RUN_END_NOTICE << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; + break; + + case (RTN_HELP_ONLY): + case (RTN_CHECK_ONLY): + return; + + default: + // Aborting because of some error + cerr << RUN_END_NOTICE << "iteration " << m_ulIter << ERROR_NOTICE << nRtn << ": \"" << strGetErrorText(nRtn) << "\", " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; + + if (m_ulIter > 1) + { + // If the run has actually started, then output all GIS files: this is very helpful in tracking down problems + m_bSaveGISThisIter = true; + m_nGISSave = 998; // Will get incremented to 999 when we write the files + bSaveAllRasterGISFiles(); + bSaveAllVectorGISFiles(); + } + + // Write the error message to the logfile and to stdout + if (LogStream && LogStream.is_open()) + { + LogStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; + LogStream.flush(); + } + + if (OutStream && OutStream.is_open()) + { + OutStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << put_time(localtime(&m_tSysEndTime), "%T %A %d %B %Y") << endl; + OutStream.flush(); + } + } + +#ifdef __GNUG__ + if (isatty(fileno(stdout))) + { + // Stdout is connected to a tty, so not running as a background job + // cout << endl + // << PRESS_KEY; + // cout.flush(); + // getchar(); + } + else + { + // Stdout is not connected to a tty, so must be running in the background; if we have something entered for the email address, then send an email + if (! m_strMailAddress.empty()) + { + cout << SEND_EMAIL << m_strMailAddress << endl; + + string strCmd("echo \""); + + stringstream ststrTmp; + ststrTmp << put_time(localtime(&m_tSysEndTime), "%T on %A %d %B %Y") << endl; + + // Send an email using Linux/Unix mail command + if (RTN_OK == nRtn) + { + // Finished normally + strCmd.append("Simulation "); + strCmd.append(m_strRunName); + strCmd.append(", running on "); + strCmd.append(strGetComputerName()); + strCmd.append(", completed normally at "); + strCmd.append(ststrTmp.str()); + strCmd.append("\" | mail -s \""); + strCmd.append(PROGRAM_NAME); + strCmd.append(": normal completion\" "); + strCmd.append(m_strMailAddress); + } + else + { + // Error, so give some information to help debugging + strCmd.append("Simulation "); + strCmd.append(m_strRunName); + strCmd.append(", running on "); + strCmd.append(strGetComputerName()); + strCmd.append(", aborted with error code "); + strCmd.append(to_string(nRtn)); + strCmd.append(": "); + strCmd.append(strGetErrorText(nRtn)); + strCmd.append(" at timestep "); + strCmd.append(to_string(m_ulIter)); + strCmd.append(" ("); + strCmd.append(strDispSimTime(m_dSimElapsed)); + strCmd.append(").\n\nThis message sent at "); + strCmd.append(ststrTmp.str()); + strCmd.append("\" | mail -s \""); + strCmd.append(PROGRAM_NAME); + strCmd.append(": ERROR\" "); + strCmd.append(m_strMailAddress); + } + + int const nRet = system(strCmd.c_str()); + + if (WEXITSTATUS(nRet) != 0) + cerr << ERR << EMAIL_ERROR << endl; + } + } +#endif +} + +//=============================================================================================================================== +//! Changes all forward slashes in the input string to backslashes, leaving the original unchanged +//=============================================================================================================================== +string CSimulation::pstrChangeToBackslash(string const* strIn) +{ + string strOut(*strIn); + strOut.replace(strOut.begin(), strOut.end(), '/', '\\'); + return strOut; +} + +//=============================================================================================================================== +//! Swaps all backslashes in the input string to forward slashes, leaving the original unchanged +//=============================================================================================================================== +string CSimulation::pstrChangeToForwardSlash(string const* strIn) +{ + string strOut(*strIn); + strOut.replace(strOut.begin(), strOut.end(), '\\', '/'); + return strOut; +} + +//=============================================================================================================================== +//! Trims whitespace from the left side of a string, does not change the original string +//=============================================================================================================================== +string CSimulation::strTrimLeft(string const* strIn) +{ + // Trim leading spaces + size_t const nStartpos = strIn->find_first_not_of(" \t"); + + if (nStartpos == string::npos) + return *strIn; + + else + return strIn->substr(nStartpos); +} + +//=============================================================================================================================== +//! Trims whitespace from the right side of a string, does not change the original string +//=============================================================================================================================== +string CSimulation::strTrimRight(string const* strIn) +{ + string strTmp(*strIn); + + // Remove any stray carriage returns (can happen if file was edited in Windows) + strTmp.erase(remove(strTmp.begin(), strTmp.end(), '\r'), strTmp.end()); + + // Trim trailing spaces + size_t const nEndpos = strTmp.find_last_not_of(" \t"); + + if (nEndpos == string::npos) + return strTmp; + + else + return strTmp.substr(0, nEndpos + 1); +} + +//=============================================================================================================================== +//! Trims whitespace from both sides of a string, does not change the original string +//=============================================================================================================================== +string CSimulation::strTrim(string const* strIn) +{ + string strTmp = *strIn; + + // Remove any stray carriage returns (can happen if file was edited in Windows) + strTmp.erase(remove(strTmp.begin(), strTmp.end(), '\r'), strTmp.end()); + + // Trim trailing spaces + size_t nPos = strTmp.find_last_not_of(" \t"); + + if (nPos != string::npos) + strTmp.resize(nPos + 1); + + // Trim leading spaces + nPos = strTmp.find_first_not_of(" \t"); + + if (nPos != string::npos) + strTmp = strTmp.substr(nPos); + + return strTmp; +} + +//=============================================================================================================================== +//! Returns the lower case version of an string, leaving the original unchanged +//=============================================================================================================================== +string CSimulation::strToLower(string const* strIn) +{ + string strOut = *strIn; + transform(strIn->begin(), strIn->end(), strOut.begin(), tolower); + return strOut; +} + +//=============================================================================================================================== +// Returns the upper case version of an string, leaving the original unchanged +//=============================================================================================================================== +// string CSimulation::strToUpper(string const* strIn) +// { +// string strOut = *strIn; +// transform(strIn->begin(), strIn->end(), strOut.begin(), toupper); +// return strOut; +// } + +//=============================================================================================================================== +//! Returns a string with a substring removed, and with whitespace trimmed +//=============================================================================================================================== +string CSimulation::strRemoveSubstr(string* pStrIn, string const* pStrSub) +{ + size_t const nPos = pStrIn->find(*pStrSub); + + if (nPos != string::npos) + { + // OK, found the substring + pStrIn->replace(nPos, pStrSub->size(), ""); + return strTrim(pStrIn); + } + + else + { + // If not found, return the string unchanged + return *pStrIn; + } +} + +//=============================================================================================================================== +//! From http://stackoverflow.com/questions/236129/split-a-string-in-c They implement (approximately) Python's split() function. This first version puts the results into a pre-constructed string vector. It ignores empty items +//=============================================================================================================================== +vector* CSimulation::VstrSplit(string const* s, char const delim, vector* elems) +{ + stringstream ss(*s); + string item; + + while (getline(ss, item, delim)) + { + if (!item.empty()) + elems->push_back(item); + } + + return elems; +} + +//=============================================================================================================================== +//! From http://stackoverflow.com/questions/236129/split-a-string-in-c They implement (approximately) Python's split() function. This second version returns a new string vector (it calls the first version) +//=============================================================================================================================== +vector CSimulation::VstrSplit(string const* s, char const delim) +{ + vector elems; + VstrSplit(s, delim, &elems); + return elems; +} + +// //=============================================================================================================================== +// //! Calculates the vector cross product of three points +// //=============================================================================================================================== +// double CSimulation::dCrossProduct(double const dX1, double const dY1, double const dX2, double const dY2, double const dX3, double const dY3) +// { +// // Based on code at http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/clockwise.htm +// return (dX2 - dX1) * (dY3 - dY2) - ((dY2 - dY1) * (dX3 - dX2)); +// } + +// //=============================================================================================================================== +// //! Calculates the mean of a pointer to a vector of doubles +// //=============================================================================================================================== +// double CSimulation::dGetMean(vector const* pV) +// { +// double dSum = accumulate(pV->begin(), pV->end(), 0.0); +// double dMean = dSum / static_cast(pV->size()); +// return dMean; +// } + +// //=============================================================================================================================== +// //! Calculates the standard deviation of a pointer to a vector of doubles. From http://stackoverflow.com/questions/7616511/calculate-mean-and-standard-deviation-from-a-vector-of-samples-in-c-using-boos +// //=============================================================================================================================== +// double CSimulation::dGetStdDev(vector const* pV) +// { +// double dSum = accumulate(pV->begin(), pV->end(), 0.0); +// double dMean = dSum / static_cast(pV->size()); +// +// double dSqSum = inner_product(pV->begin(), pV->end(), pV->begin(), 0.0); +// double dStdDev = sqrt(dSqSum / static_cast(pV->size()) - dMean * dMean); +// +// return dStdDev; +// } + +//=============================================================================================================================== +//! Appends a CGeom2DIPoint to a vector, making sure that the new end point touches the previous end point i.e. that there is no gap between the two points +//=============================================================================================================================== +void CSimulation::AppendEnsureNoGap(vector* pVPtiPoints, CGeom2DIPoint const* pPti) +{ + int const nX = pPti->nGetX(); + int const nY = pPti->nGetY(); + int const nXLast = pVPtiPoints->back().nGetX(); + int const nYLast = pVPtiPoints->back().nGetY(); + int const nXDiff = nX - nXLast; + int const nYDiff = nY - nYLast; + int const nXDiffA = tAbs(nXDiff); + int const nYDiffA = tAbs(nYDiff); + int const nDiff = tMax(nXDiffA, nYDiffA); + + if (nDiff > 1) + { + // We have a gap + double + dXInc = 0, + dYInc = 0; + + if (nXDiffA > 1) + dXInc = static_cast(nXDiff) / nDiff; + + if (nYDiffA > 1) + dYInc = static_cast(nYDiff) / nDiff; + + for (int n = 1; n < nDiff; n++) + { + CGeom2DIPoint const Pti(nXLast + nRound(n * dXInc), nYLast + nRound(n * dYInc)); + pVPtiPoints->push_back(Pti); + } + } + + pVPtiPoints->push_back(CGeom2DIPoint(nX, nY)); +} + +//=============================================================================================================================== +//! Calculates a Dean equilibrium profile h(y) = A * y^(2/3) where h(y) is the distance below the highest point in the Dean profile at a distance y from the landward start of the profile +//=============================================================================================================================== +void CSimulation::CalcDeanProfile(vector* pdVDeanProfile, double const dInc, double const dDeanTopElev, double const dA, bool const bDeposition, int const nSeawardOffset, double const dStartCellElev) +{ + double dDistFromProfileStart = 0; + + if (bDeposition) + { + // This Dean profile is for deposition i.e. seaward displacement of the profile + pdVDeanProfile->at(0) = dStartCellElev; // Is coast elevation + + for (int n = 1; n < static_cast(pdVDeanProfile->size()); n++) + { + if (n <= nSeawardOffset) + // As we extend the profile seaward, the elevation of any points coastward of the new coast point of the Dean profile are set to the elevation of the original coast point + pdVDeanProfile->at(n) = dStartCellElev; + else + { + double const dDistBelowTop = dA * pow(dDistFromProfileStart, DEAN_POWER); + pdVDeanProfile->at(n) = dDeanTopElev - dDistBelowTop; + + dDistFromProfileStart += dInc; + } + } + } + else + { + // This Dean profile is for erosion i.e. landward displacement of the profile + for (int n = 0; n < static_cast(pdVDeanProfile->size()); n++) + { + double const dDistBelowTop = dA * pow(dDistFromProfileStart, DEAN_POWER); + pdVDeanProfile->at(n) = dDeanTopElev - dDistBelowTop; + + dDistFromProfileStart += dInc; + } + } +} + +//=============================================================================================================================== +//! Calculate the total elevation difference between every point in two elevation profiles (first profile - second profile) +//=============================================================================================================================== +double CSimulation::dSubtractProfiles(vector const* pdVFirstProfile, vector const* pdVSecondProfile, vector const* pbVIsValid) +{ + double dTotElevDiff = 0; + + // Note that this assumes that all three vectors are of equal length, should really check this + for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) + { + if (pbVIsValid->at(n)) + { + double const dProfileDiff = pdVFirstProfile->at(n) - pdVSecondProfile->at(n); + + dTotElevDiff += dProfileDiff; + } + } + + // // DEBUG CODE ----------------------------------------------------- + // LogStream << endl; + // LogStream << "First profile = "; + // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) + // { + // LogStream << pdVFirstProfile->at(n) << " "; + // } + // LogStream << endl; + // LogStream << "Second profile = "; + // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) + // { + // LogStream << pdVSecondProfile->at(n) << " "; + // } + // LogStream << endl; + // LogStream << "Difference = "; + // for (int n = 0; n < static_cast(pdVFirstProfile->size()); n++) + // { + // LogStream << pdVFirstProfile->at(n) - pdVSecondProfile->at(n) << " "; + // } + // LogStream << endl; + // // DEBUG CODE ----------------------------------------------------- + + return dTotElevDiff; +} + +//=============================================================================================================================== +//! Calculate the depth of closure +//=============================================================================================================================== +void CSimulation::CalcDepthOfClosure(void) +{ + double + dDeepWaterWaveHeight, + dDeepWaterPeriod; + + if (m_bSingleDeepWaterWaveValues) + { + dDeepWaterWaveHeight = m_dAllCellsDeepWaterWaveHeight; + dDeepWaterPeriod = m_dAllCellsDeepWaterWavePeriod; + } + + else + { + dDeepWaterWaveHeight = m_dMaxUserInputWaveHeight; + dDeepWaterPeriod = m_dMaxUserInputWavePeriod; + } + + // TODO 051 Calculate depth of closure using 'average of the maximum values observed during a typical year' + // dL = 2.28 * Hsx − (68.5 * Hsx^2 / (g * Tsx^2)) + // where: + // Hsx is the nearshore storm wave height that is exceeded only 12 hours each year + // Tsx is the associated wave period + // from Hallermeier, R.J. (1978). Uses for a calculated limit depth to beach erosion. Proc. 16th Coastal Engineering Conf., ASCE, New York. Pp 1493 - 1512 + // + // For the time being, and since we assume wave height and period constant just use the actual wave height and period to calculate the depth of closure + // m_dDepthOfClosure = (2.28 * dDeepWaterWaveHeight) - (68.5 * dDeepWaterWaveHeight * dDeepWaterWaveHeight / (m_dG * dDeepWaterPeriod * dDeepWaterPeriod)); + + // An alternative (which produces smaller depth of closure estimates) is Birkemeier (1985) TODO 007 Full reference needed + // dL = 1.75 * Hsx - (57.9 * Hsx^2/ (g * Tsx^2)) + m_dDepthOfClosure = (1.75 * dDeepWaterWaveHeight) - (57.9 * dDeepWaterWaveHeight * dDeepWaterWaveHeight / (m_dG * dDeepWaterPeriod * dDeepWaterPeriod)); +} + +// //=============================================================================================================================== +// //! Tests a reference to a string to see if it is numeric (modified from https://tfetimes.com/c-determine-if-a-string-is-numeric/) +// //=============================================================================================================================== +// bool CSimulation::bIsNumeric(string const*strIn) +// { +// return all_of(strIn->begin(), strIn->end(), isdigit); +// } + +//=============================================================================================================================== +//! Parses a date string into days, months, and years, and checks each of them +//=============================================================================================================================== +bool CSimulation::bParseDate(string const* strDate, int& nDay, int& nMonth, int& nYear) +{ + vector VstrTmp = VstrSplit(strDate, SLASH); + + if (VstrTmp.size() < 3) + { + cerr << "date string must include day, month, and year '" << strDate << "'" << endl; + return false; + } + + // Sort out day + if (! bIsStringValidInt(VstrTmp[0])) + { + cerr << "invalid integer for day in date '" << strDate << "'" << endl; + return false; + } + + nDay = stoi(VstrTmp[0]); + + if ((nDay < 1) || (nDay > 31)) + { + cerr << "day must be between 1 and 31 in date '" << strDate << "'" << endl; + return false; + } + + // Sort out month + if (! bIsStringValidInt(VstrTmp[1])) + { + cerr << "invalid integer for month in date '" << strDate << "'" << endl; + return false; + } + + nMonth = stoi(VstrTmp[1]); + + if ((nMonth < 1) || (nMonth > 12)) + { + cerr << "month must be between 1 and 12 in date '" << strDate << "'" << endl; + return false; + } + + // Sort out year + if (! bIsStringValidInt(VstrTmp[2])) + { + cerr << "invalid integer for year in date '" << strDate << "'" << endl; + return false; + } + + nYear = stoi(VstrTmp[2]); + + if (nYear < 0) + { + cerr << "year must be > 0 in date '" << strDate << "'" << endl; + return false; + } + + return true; +} + +//=============================================================================================================================== +//! Parses a time string into hours, minutes, and seconds, and checks each of them +//=============================================================================================================================== +bool CSimulation::bParseTime(string const* strTime, int& nHour, int& nMin, int& nSec) +{ + vector VstrTmp = VstrSplit(strTime, DASH); + + if (VstrTmp.size() < 3) + { + cerr << "time string must include hours, minutes, and seconds '" << strTime << "'" << endl; + return false; + } + + // Sort out hour + if (! bIsStringValidInt(VstrTmp[0])) + { + cerr << "invalid integer for hours in time '" << strTime << "'" << endl; + return false; + } + + nHour = stoi(VstrTmp[0]); + + if ((nHour < 0) || (nHour > 23)) + { + cerr << "hour must be between 0 and 23 in time '" << strTime << "'" << endl; + return false; + } + + // Sort out minutes + if (! bIsStringValidInt(VstrTmp[1])) + { + cerr << "invalid integer for minutes in time '" << strTime << "'" << endl; + return false; + } + + nMin = stoi(VstrTmp[1]); + + if ((nMin < 0) || (nMin > 59)) + { + cerr << "minutes must be betwen 0 and 59 in time '" << strTime << "'" << endl; + return false; + } + + // Sort out seconds + if (! bIsStringValidInt(VstrTmp[2])) + { + cerr << "invalid integer for seconds in time '" << strTime << "'" << endl; + return false; + } + + nSec = stoi(VstrTmp[2]); + + if ((nSec < 0) || (nSec > 59)) + { + cerr << "seconds must be between 0 and 59 in time '" << strTime << "'" << endl; + return false; + } + + return true; +} + +//=============================================================================================================================== +//! For sediment input events, parses a string that may be relative (a number of hours or days after the start of the simulation), or absolute (a time/date in the format hh-mm-ss dd/mm/yyyy). Returns the timestep in which the sediment input event occurs +//=============================================================================================================================== +unsigned long CSimulation::ulConvertToTimestep(string const* pstrIn) const +{ + unsigned long ulTimeStep = 0; + + // Convert to lower case, remove leading and trailing whitespace + string strDate = strToLower(pstrIn); + strDate = strTrim(&strDate); + + if (strDate.find("hour") != string::npos) + { + // OK, this is a number of hours (a relative time, from the start of simulation) + vector VstrTmp = VstrSplit(&strDate, SPACE); + + if ((VstrTmp.size() < 2) || (! bIsStringValidInt(VstrTmp[0]))) + { + cerr << "Error in number of hours '" + strDate + "' for sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + double const dHours = stod(strTrim(&VstrTmp[0])); + + if (dHours > m_dSimDuration) + { + cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + ulTimeStep = static_cast(dRound(dHours / m_dTimeStep)); + } + + else if (strDate.find("day") != string::npos) + { + // OK, this is a number of days (a relative time, from the start of simulation) + vector VstrTmp = VstrSplit(&strDate, SPACE); + + if ((VstrTmp.size() < 2) || (! bIsStringValidInt(VstrTmp[0]))) + { + cerr << "Error in number of days '" + strDate + "' for sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + double const dHours = stod(strTrim(&VstrTmp[0])) * 24; + + if (dHours > m_dSimDuration) + { + cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + ulTimeStep = static_cast(dRound(dHours / m_dTimeStep)); + } + + else + { + // This is an absolute time/date in the format hh-mm-ss dd/mm/yyyy + vector VstrTmp = VstrSplit(&strDate, SPACE); + + if (VstrTmp.size() < 2) + { + cerr << "Error in time/date '" + strDate + "' of sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + int nHour = 0; + int nMin = 0; + int nSec = 0; + + // OK, first sort out the time + if (! bParseTime(&VstrTmp[0], nHour, nMin, nSec)) + { + cerr << "Error in time '" + VstrTmp[0] + "' of sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + int nDay = 0; + int nMonth = 0; + int nYear = 0; + + // Now sort out the time + if (! bParseDate(&VstrTmp[1], nDay, nMonth, nYear)) + { + cerr << "Error in date '" + VstrTmp[1] + "' of sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + // This is modified from https://stackoverflow.com/questions/14218894/number-of-days-between-two-dates-c + struct tm tmSimStart = {}; + tmSimStart.tm_sec = m_nSimStartSec; + tmSimStart.tm_min = m_nSimStartMin; + tmSimStart.tm_hour = m_nSimStartHour; + tmSimStart.tm_mday = m_nSimStartDay; + tmSimStart.tm_mon = m_nSimStartMonth - 1; + tmSimStart.tm_year = m_nSimStartYear - 1900; + + struct tm tmSimEvent = {}; + tmSimEvent.tm_sec = nSec; + tmSimEvent.tm_min = nMin; + tmSimEvent.tm_hour = nHour; + tmSimEvent.tm_mday = nDay; + tmSimEvent.tm_mon = nMonth - 1; + tmSimEvent.tm_year = nYear - 1900; + + time_t const tStart = mktime(&tmSimStart); + time_t const tEvent = mktime(&tmSimEvent); + + if (tStart == (time_t)(-1)) + { + cerr << "Error in simulation start time/date" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + if (tEvent == (time_t)(-1)) + { + cerr << "Error in time/date '" + strDate + "' of sediment input event" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + double const dHours = difftime(tEvent, tStart) / (60 * 60); + + if (dHours < 0) + { + cerr << "Sediment input event '" + strDate + "' occurs before start of simulation" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + if (dHours > m_dSimDuration) + { + cerr << "Sediment input event '" + strDate + "' occurs after end of simulation" << endl; + return SEDIMENT_INPUT_EVENT_ERROR; + } + + ulTimeStep = static_cast(dHours / m_dTimeStep); + } + + return ulTimeStep; +} + +//=============================================================================================================================== +//! Returns true if the cell is an intervention +//=============================================================================================================================== +bool CSimulation::bIsInterventionCell(int const nX, int const nY) const +{ + int const nCat = m_pRasterGrid->m_Cell[nX][nY].pGetLandform()->nGetLFCategory(); + if ((nCat == LF_INTERVENTION_STRUCT) || (nCat == LF_INTERVENTION_NON_STRUCT)) + return true; + + return false; +} + +//=============================================================================================================================== +//! Do end-of-run memory clearance +//=============================================================================================================================== +void CSimulation::DoEndOfRunDeletes(void) +{ + // Clear all vector coastlines, profiles, and polygons + m_VCoast.clear(); + + // m_VFloodWaveSetup.clear(); + m_VFloodWaveSetupSurge.clear(); + m_VFloodWaveSetupSurgeRunup.clear(); +} + +//=============================================================================================================================== +//! Calculate Mean High Water (MHW) elevation for a given duration (in days). This is a tidal datum determined from the arithmetic mean of the high water heights observed each tidal day +//=============================================================================================================================== +void CSimulation::CalcMHWElevation(int const nTideDataCount) +{ + // Calculate the number of tide values to read + int const nTidevaluesPerDay = tMax(nRound(24 / m_dTimeStep), 1); + int const nTideValuesToRead = nTidevaluesPerDay * NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC; + + int const nNumTideValues = static_cast(m_VdTideData.size()); + + // Now read the tide data (note that this assumes that the first line of the tide data is the first tide reading of the day) + int nThisCount = nTideDataCount; + double dTotMaxTide = 0; + for (int n = 0; n < nTideValuesToRead; n++) + { + // Read in this days's tide data + double dDayMaxTide = -DBL_MAX; + for (int m = 0; m < nTidevaluesPerDay; m++) + { + // If necessary, wrap the tide data, i.e. start again with the first line of the tide data if we do not have enough + if (nThisCount > nNumTideValues - 1) + nThisCount = 0; + + double const dThisTideData = m_VdTideData[nThisCount]; + + if (dThisTideData > dDayMaxTide) + dDayMaxTide = dThisTideData; + + nThisCount++; + } + + // We have the max tide for the day, so increment the total of highest daily tides + dTotMaxTide += dDayMaxTide; + } + + // Now calculate the average max tide for the next NUM_DAYS_FOR_MEAN_HIGH_WATER_CALC days + double const dMaxTideAvg = dTotMaxTide / nTideValuesToRead; + + // Finally, calculate MHW for this iteration (includes long-term SWL change) + m_dThisIterMHWElev = m_dThisIterMeanSWL + dMaxTideAvg; + + // And set the apex elevation of any new cliff notches (i.e. cliff notches which will be created during this timestep) to be at or slightly above MHW level + m_dThisIterNewNotchApexElev = m_dThisIterMHWElev + m_dNotchApexAboveMHW; + + // LogStream << m_ulIter << ": this-iteration MHW elevation = " << m_dThisIterMHWElev << " elevation of apex of new cliff notches = " << m_dThisIterNewNotchApexElev << endl; +} diff --git a/src/yaml_parser.h b/src/yaml_parser.h index 4363c65f7..e4b557629 100644 --- a/src/yaml_parser.h +++ b/src/yaml_parser.h @@ -1,99 +1,99 @@ -/*! - - \file yaml_parser.h - \brief Simple YAML parser for CoastalME configuration files - \details A lightweight YAML parser using only standard C++ library, designed specifically for CoastalME configuration needs - \author David Favis-Mortlock - \author Andres Payo - \date 2025 - \copyright GNU General Public License - -*/ - -/* ============================================================================================================================== - - This file is part of CoastalME, the Coastal Modelling Environment. - - CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -==============================================================================================================================*/ -#ifndef YAML_PARSER_H -#define YAML_PARSER_H - -#include -#include -#include -#include - -using std::ifstream; -using std::map; -using std::string; -using std::vector; - -//! Simple YAML node class to represent parsed values -class CYamlNode -{ - private: - string m_strValue; - map m_mapChildren; - vector m_vecChildren; - bool m_bIsSequence; - - public: - CYamlNode(); - ~CYamlNode(); - - void SetValue(string const& strValue); - void AddChild(string const& strKey, CYamlNode const& node); - void AddSequenceItem(CYamlNode const& node); - - string GetValue() const; - bool HasChild(string const& strKey) const; - CYamlNode GetChild(string const& strKey) const; - vector GetSequence() const; - bool IsSequence() const; - int GetSequenceSize() const; - - // Convenience methods for common types - int GetIntValue(int nDefault = 0) const; - unsigned long GetULongValue(unsigned long nDefault = 0) const; - double GetDoubleValue(double dDefault = 0.0) const; - bool GetBoolValue(bool bDefault = false) const; - vector GetStringSequence() const; -}; - -//! Simple YAML parser class -class CYamlParser -{ - private: - CYamlNode m_RootNode; - string m_strFileName; - int m_nCurrentLine; - string m_strError; - - // Helper methods - int nGetIndentLevel(string const& strLine) const; - string strTrimLeft(string const& strLine) const; - string strTrimRight(string const& strLine) const; - string strTrim(string const& strLine) const; - bool bIsComment(string const& strLine) const; - bool bIsEmpty(string const& strLine) const; - bool bParseLine(string const& strLine, string& strKey, string& strValue, bool& bIsSequence) const; - string strRemoveQuotes(string const& strValue) const; - CYamlNode ParseSection(ifstream& fileStream, int nBaseIndent); - - public: - CYamlParser(); - ~CYamlParser(); - - bool bParseFile(string const& strFileName); - CYamlNode GetRoot() const; - string GetError() const; - bool bHasError() const; -}; - -#endif // YAML_PARSER_H +/*! + + \file yaml_parser.h + \brief Simple YAML parser for CoastalME configuration files + \details A lightweight YAML parser using only standard C++ library, designed specifically for CoastalME configuration needs + \author David Favis-Mortlock + \author Andres Payo + \date 2025 + \copyright GNU General Public License + +*/ + +/* ============================================================================================================================== + + This file is part of CoastalME, the Coastal Modelling Environment. + + CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +==============================================================================================================================*/ +#ifndef YAML_PARSER_H +#define YAML_PARSER_H + +#include +#include +#include +#include + +using std::ifstream; +using std::map; +using std::string; +using std::vector; + +//! Simple YAML node class to represent parsed values +class CYamlNode +{ + private: + string m_strValue; + map m_mapChildren; + vector m_vecChildren; + bool m_bIsSequence; + + public: + CYamlNode(); + ~CYamlNode(); + + void SetValue(string const& strValue); + void AddChild(string const& strKey, CYamlNode const& node); + void AddSequenceItem(CYamlNode const& node); + + string GetValue() const; + bool HasChild(string const& strKey) const; + CYamlNode GetChild(string const& strKey) const; + vector GetSequence() const; + bool IsSequence() const; + int GetSequenceSize() const; + + // Convenience methods for common types + int GetIntValue(int nDefault = 0) const; + unsigned long GetULongValue(unsigned long nDefault = 0) const; + double GetDoubleValue(double dDefault = 0.0) const; + bool GetBoolValue(bool bDefault = false) const; + vector GetStringSequence() const; +}; + +//! Simple YAML parser class +class CYamlParser +{ + private: + CYamlNode m_RootNode; + string m_strFileName; + int m_nCurrentLine; + string m_strError; + + // Helper methods + int nGetIndentLevel(string const& strLine) const; + string strTrimLeft(string const& strLine) const; + string strTrimRight(string const& strLine) const; + string strTrim(string const& strLine) const; + bool bIsComment(string const& strLine) const; + bool bIsEmpty(string const& strLine) const; + bool bParseLine(string const& strLine, string& strKey, string& strValue, bool& bIsSequence) const; + string strRemoveQuotes(string const& strValue) const; + CYamlNode ParseSection(ifstream& fileStream, int nBaseIndent); + + public: + CYamlParser(); + ~CYamlParser(); + + bool bParseFile(string const& strFileName); + CYamlNode GetRoot() const; + string GetError() const; + bool bHasError() const; +}; + +#endif // YAML_PARSER_H